fcr-core
Version:
Core APIs for building online scenes
273 lines (271 loc) • 8.35 kB
JavaScript
import "core-js/modules/es.array.push.js";
import { md5 } from 'js-md5';
import { ApplianceNames, FcrBoardShape, FcrBoardToolType, ShapeType } from './enum';
import { EStrokeType } from '@netless/appliance-plugin';
export const heightPerTool = 36;
export const heightPerColor = 18;
export const defaultToolsRetain = heightPerTool * 6;
export const verticalPadding = 10;
export const sceneNavHeight = heightPerTool + verticalPadding;
export const widgetContainerClassName = 'netless-whiteboard-wrapper';
export const layoutContentClassName = 'fcr-layout-content-main-view';
export const videoRowClassName = 'fcr-layout-content-video-list-row';
export const toolbarClassName = 'fcr-board-toolbar';
export const windowClassName = 'netless-whiteboard-wrapper';
export const WINDOW_TITLE_HEIGHT = 28;
// width / height
export const WINDOW_ASPECT_RATIO = 1836 / 847;
export const WINDOW_MIN_SIZE = {
width: 653,
height: 336
};
export const WINDOW_REMAIN_SIZE = {
width: 783,
height: 388
};
export const WINDOW_REMAIN_POSITION = {
x: 0,
y: 171
};
/**
* 根据
* @param imageInnerSize
* @returns
*/
export const getImageSize = (imageInnerSize, containerSize) => {
const windowSize = containerSize;
const widthHeightProportion = imageInnerSize.width / imageInnerSize.height;
const maxSize = 960;
if (imageInnerSize.width > maxSize && windowSize.width > maxSize || imageInnerSize.height > maxSize && windowSize.height > maxSize) {
if (widthHeightProportion > 1) {
return {
width: maxSize,
height: maxSize / widthHeightProportion
};
} else {
return {
width: maxSize * widthHeightProportion,
height: maxSize
};
}
} else {
if (imageInnerSize.width > windowSize.width || imageInnerSize.height > windowSize.height) {
if (widthHeightProportion > 1) {
return {
width: windowSize.width,
height: windowSize.width / widthHeightProportion
};
} else {
return {
width: windowSize.height * widthHeightProportion,
height: windowSize.height
};
}
} else {
return {
width: imageInnerSize.width,
height: imageInnerSize.height
};
}
}
};
/**
*
* @param url
* @returns
*/
export const fetchImageInfoByUrl = async (url, containerSize) => {
try {
const res = await fetch(url);
const blob = await res.blob();
const contentType = blob.type;
const image = new Image();
const reader = new FileReader();
const file = new File([blob], url, {
type: contentType
});
const result = await new Promise(resolve => {
reader.readAsDataURL(blob);
reader.onload = () => {
image.addEventListener('load', () => {
const uuid = md5(reader.result);
const res = getImageSize(image, containerSize);
const result = {
width: res.width,
height: res.height,
file: file,
url,
uuid
};
resolve(result);
}, false);
image.src = reader.result;
};
});
return result;
} catch (err) {
throw err;
}
};
export const mergeCanvasImage = async scenes => {
let width = 0,
height = 0;
const bigCanvas = document.createElement('canvas');
const ctx = bigCanvas.getContext('2d');
const canvasArray = [];
for (const canvasPromise of scenes) {
const canvas = await canvasPromise();
if (canvas) {
width = Math.max(canvas.width, width);
height = Math.max(canvas.height, height);
canvasArray.push(canvas);
}
}
bigCanvas.setAttribute('width', `${width}`);
bigCanvas.setAttribute('height', `${height * canvasArray.length}`);
canvasArray.forEach((canvas, index) => {
ctx && ctx.drawImage(canvas, 0, index * height, width, height);
});
return bigCanvas;
};
export const textColors = ['#ffffff', '#9b9b9b', '#4a4a4a', '#000000', '#d0021b', '#f5a623', '#f8e71c', '#7ed321', '#9013fe', '#50e3c2', '#0073ff', '#ffc8e2'];
export const defaultStrokeColor = {
r: 0,
g: 115,
b: 255
};
export const defaultTextSize = 24;
export const mediaMimeTypes = {
opus: 'video/ogg',
ogv: 'video/ogg',
mp4: 'video/mp4',
mov: 'video/mp4',
m4v: 'video/mp4',
mkv: 'video/x-matroska',
m4a: 'audio/mp4',
mp3: 'audio/mpeg',
aac: 'audio/aac',
caf: 'audio/x-caf',
flac: 'audio/flac',
oga: 'audio/ogg',
wav: 'audio/wav',
m3u8: 'application/x-mpegURL',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
gif: 'image/gif',
png: 'image/png',
svg: 'image/svg+xml',
webp: 'image/webp'
};
export const convertToNetlessBoardTool = tool => {
switch (tool) {
case FcrBoardToolType.SELECTOR:
return [ApplianceNames.selector];
case FcrBoardToolType.ERASER:
return [ApplianceNames.pencilEraser];
case FcrBoardToolType.LASER_POINTER:
return [ApplianceNames.laserPointer];
case FcrBoardToolType.HAND:
return [ApplianceNames.hand];
case FcrBoardToolType.TEXT:
return [ApplianceNames.text];
case FcrBoardToolType.ARROW:
return [ApplianceNames.arrow];
case FcrBoardToolType.RECTANGLE:
return [ApplianceNames.rectangle];
case FcrBoardToolType.ELLIPSE:
return [ApplianceNames.ellipse];
case FcrBoardToolType.STRAIGHT:
return [ApplianceNames.straight];
case FcrBoardToolType.CURVE:
return [ApplianceNames.pencil];
case FcrBoardToolType.TRIANGLE:
return [ApplianceNames.shape, ShapeType.Triangle];
case FcrBoardToolType.PENTAGRAM:
return [ApplianceNames.shape, ShapeType.Pentagram];
case FcrBoardToolType.RHOMBUS:
return [ApplianceNames.shape, ShapeType.Rhombus];
case FcrBoardToolType.DOTTED_LINE:
case FcrBoardToolType.LONG_DOTTED_LINE:
return [ApplianceNames.straight];
case FcrBoardToolType.NONE:
return [ApplianceNames.clicker];
default:
return [];
}
};
export const convertToNetlessStorkeType = type => {
switch (type) {
case FcrBoardToolType.DOTTED_LINE:
return EStrokeType.Dotted;
case FcrBoardToolType.LONG_DOTTED_LINE:
return EStrokeType.LongDotted;
default:
return EStrokeType.Normal;
}
};
export const convertToFcrBoardToolShape = (tool, shape) => {
switch (tool) {
case ApplianceNames.selector:
return [FcrBoardToolType.SELECTOR];
case ApplianceNames.eraser:
return [FcrBoardToolType.ERASER];
case ApplianceNames.laserPointer:
return [FcrBoardToolType.LASER_POINTER];
case ApplianceNames.text:
return [FcrBoardToolType.TEXT];
case ApplianceNames.hand:
return [FcrBoardToolType.HAND];
}
switch (`${tool || ''}${shape || ''}`) {
case `${ApplianceNames.rectangle}`:
return [, FcrBoardShape.Rectangle];
case `${ApplianceNames.ellipse}`:
return [, FcrBoardShape.Ellipse];
case `${ApplianceNames.straight}`:
return [, FcrBoardShape.Straight];
case `${ApplianceNames.arrow}`:
return [, FcrBoardShape.Arrow];
case `${ApplianceNames.pencil}`:
return [, FcrBoardShape.Curve];
case `${ApplianceNames.shape}${ShapeType.Triangle}`:
return [, FcrBoardShape.Triangle];
case `${ApplianceNames.shape, ShapeType.Pentagram}`:
return [, FcrBoardShape.Pentagram];
case `${ApplianceNames.shape}${ShapeType.Rhombus}`:
return [, FcrBoardShape.Rhombus];
}
return [];
};
export const hexColorToWhiteboardColor = val => {
const pattern = /^(#?)[a-fA-F0-9]{6}$/; // 16进制颜色校验规则
if (!pattern.test(val)) {
return [255, 255, 255];
}
const v = val.replace(/#/, '');
const rgbArr = [];
for (let i = 0; i < 3; i++) {
const item = v.substring(i * 2, i * 2 + 2);
const num = parseInt(item, 16);
rgbArr.push(num);
}
return rgbArr;
};
export const src2DataURL = src => {
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const image = new Image();
image.onload = () => {
canvas.setAttribute('width', `${image.width}`);
canvas.setAttribute('height', `${image.height}`);
ctx?.drawImage(image, 0, 0);
resolve(canvas.toDataURL('image/jpeg', 0.8));
};
image.onerror = () => {
reject('error');
};
image.crossOrigin = 'anonymous';
image.src = src;
});
};