feat: enhance image and path in renderer and optimize middleare ruler

This commit is contained in:
chenshenhai 2024-05-26 14:09:12 +08:00
parent 75eb760b5f
commit 809c704ed9
14 changed files with 194 additions and 108 deletions

View file

@ -405,9 +405,9 @@ export class Board<T extends BoardExtendEventMap = BoardExtendEventMap> {
const { width, height, devicePixelRatio } = newViewSize;
const { boardContent } = this.#opts;
boardContent.viewContext.$resize({ width, height, devicePixelRatio });
boardContent.helperContext.$resize({ width, height, devicePixelRatio });
boardContent.overlayContext.$resize({ width, height, devicePixelRatio });
boardContent.boardContext.$resize({ width, height, devicePixelRatio });
boardContent.underContext.$resize({ width, height, devicePixelRatio });
boardContent.underlayContext.$resize({ width, height, devicePixelRatio });
this.#viewer.drawFrame();
this.#watcher.trigger('resize', viewSize);
this.#sharer.setActiveViewSizeInfo(newViewSize);
@ -415,9 +415,9 @@ export class Board<T extends BoardExtendEventMap = BoardExtendEventMap> {
clear() {
const { boardContent } = this.#opts;
const { underContext, helperContext, viewContext, boardContext } = boardContent;
underContext.clearRect(0, 0, underContext.canvas.width, underContext.canvas.height);
helperContext.clearRect(0, 0, helperContext.canvas.width, helperContext.canvas.height);
const { underlayContext, overlayContext, viewContext, boardContext } = boardContent;
underlayContext.clearRect(0, 0, underlayContext.canvas.width, underlayContext.canvas.height);
overlayContext.clearRect(0, 0, overlayContext.canvas.width, overlayContext.canvas.height);
viewContext.clearRect(0, 0, viewContext.canvas.width, viewContext.canvas.height);
boardContext.clearRect(0, 0, boardContext.canvas.width, boardContext.canvas.height);
this.#handleClear();

View file

@ -172,17 +172,17 @@ export class Viewer extends EventEmitter<BoardViewerEventMap> implements BoardVi
const newViewSize = { ...originViewSize, ...viewSize };
const { width, height, devicePixelRatio } = newViewSize;
const { underContext, boardContext, helperContext, viewContext } = this.#opts.boardContent;
const { underlayContext, boardContext, overlayContext, viewContext } = this.#opts.boardContent;
boardContext.canvas.width = width * devicePixelRatio;
boardContext.canvas.height = height * devicePixelRatio;
boardContext.canvas.style.width = `${width}px`;
boardContext.canvas.style.height = `${height}px`;
underContext.canvas.width = width * devicePixelRatio;
underContext.canvas.height = height * devicePixelRatio;
underlayContext.canvas.width = width * devicePixelRatio;
underlayContext.canvas.height = height * devicePixelRatio;
helperContext.canvas.width = width * devicePixelRatio;
helperContext.canvas.height = height * devicePixelRatio;
overlayContext.canvas.width = width * devicePixelRatio;
overlayContext.canvas.height = height * devicePixelRatio;
viewContext.canvas.width = width * devicePixelRatio;
viewContext.canvas.height = height * devicePixelRatio;

View file

@ -11,7 +11,7 @@ const infoLineHeight = 16;
export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage> = (opts) => {
const { boardContent, calculator } = opts;
const { helperContext } = boardContent;
const { overlayContext } = boardContent;
return {
name: '@middleware/info',
@ -76,7 +76,7 @@ export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage> = (opts) =>
const h = formatNumber(elem.h, { decimalPlaces: 2 });
// // test start ----
// const ctx = helperContext;
// const ctx = overlayContext;
// ctx.beginPath();
// ctx.moveTo(rectInfo.topLeft.x, rectInfo.topLeft.y);
// ctx.lineTo(rectInfo.topRight.x, rectInfo.topRight.y);
@ -91,7 +91,7 @@ export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage> = (opts) =>
const whText = `${formatNumber(w, { decimalPlaces: 0 })}x${formatNumber(h, { decimalPlaces: 0 })}`;
const angleText = `${formatNumber(elem.angle || 0, { decimalPlaces: 0 })}°`;
drawSizeInfoText(helperContext, {
drawSizeInfoText(overlayContext, {
point: {
x: rectInfo.bottom.x,
y: rectInfo.bottom.y + infoFontSize
@ -105,7 +105,7 @@ export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage> = (opts) =>
background: infoBackground
});
drawPositionInfoText(helperContext, {
drawPositionInfoText(overlayContext, {
point: {
x: rectInfo.topLeft.x,
y: rectInfo.topLeft.y - infoFontSize * 2
@ -119,7 +119,7 @@ export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage> = (opts) =>
background: infoBackground
});
drawAngleInfoText(helperContext, {
drawAngleInfoText(overlayContext, {
point: {
x: rectInfo.top.x + infoFontSize,
y: rectInfo.top.y - infoFontSize * 2

View file

@ -8,7 +8,7 @@ import { eventChange } from '../../config';
export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStorage> = (opts) => {
const { sharer, boardContent, calculator, viewer, eventHub } = opts;
const { helperContext } = boardContent;
const { overlayContext } = boardContent;
let prevPoint: Point | null = null;
@ -244,7 +244,7 @@ export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStora
const size = { x, y, w, h };
const controller = calcLayoutSizeController(size, { viewScaleInfo, controllerSize: 10 });
drawLayoutController(helperContext, { controller, operations: activeStore.data.layout.operations || {} });
drawLayoutController(overlayContext, { controller, operations: activeStore.data.layout.operations || {} });
}
}
},

View file

@ -1,13 +1,13 @@
import type { BoardMiddleware, CoreEventMap } from '@idraw/types';
import { getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idraw/util';
import { drawRulerBackground, drawXRuler, drawYRuler, calcXRulerScaleList, calcYRulerScaleList, drawUnderGrid, drawScrollerSelectedArea } from './util';
import { drawRulerBackground, drawXRuler, drawYRuler, calcXRulerScaleList, calcYRulerScaleList, drawGrid, drawScrollerSelectedArea } from './util';
import type { DeepRulerSharedStorage } from './types';
export const middlewareEventRuler = '@middleware/show-ruler';
export const MiddlewareRuler: BoardMiddleware<DeepRulerSharedStorage, CoreEventMap> = (opts) => {
const { boardContent, viewer, eventHub, calculator } = opts;
const { helperContext, underContext } = boardContent;
const { overlayContext, underlayContext } = boardContent;
let show: boolean = true;
let showGrid: boolean = true;
@ -36,18 +36,20 @@ export const MiddlewareRuler: BoardMiddleware<DeepRulerSharedStorage, CoreEventM
if (show === true) {
const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot);
const viewSizeInfo = getViewSizeInfoFromSnapshot(snapshot);
drawScrollerSelectedArea(helperContext, { snapshot, calculator });
drawRulerBackground(helperContext, { viewScaleInfo, viewSizeInfo });
drawScrollerSelectedArea(overlayContext, { snapshot, calculator });
const xList = calcXRulerScaleList({ viewScaleInfo, viewSizeInfo });
drawXRuler(helperContext, { scaleList: xList });
drawRulerBackground(overlayContext, { viewScaleInfo, viewSizeInfo });
const yList = calcYRulerScaleList({ viewScaleInfo, viewSizeInfo });
drawYRuler(helperContext, { scaleList: yList });
const { list: xList, rulerUnit } = calcXRulerScaleList({ viewScaleInfo, viewSizeInfo });
drawXRuler(overlayContext, { scaleList: xList });
const { list: yList } = calcYRulerScaleList({ viewScaleInfo, viewSizeInfo });
drawYRuler(overlayContext, { scaleList: yList });
if (showGrid === true) {
drawUnderGrid(underContext, {
const ctx = rulerUnit === 1 ? overlayContext : underlayContext;
drawGrid(ctx, {
xList,
yList,
viewScaleInfo,

View file

@ -28,14 +28,40 @@ interface RulerScale {
isSubKeyNum: boolean;
}
function calcRulerScaleList(opts: { axis: 'X' | 'Y'; scale: number; viewLength: number; viewOffset: number }): RulerScale[] {
const limitRulerUnitList = [1, 2, 5, 10, 20, 50, 100, 200, 500];
function limitRulerUnit(unit: number): number {
unit = Math.max(limitRulerUnitList[0], Math.min(unit, limitRulerUnitList[limitRulerUnitList.length - 1]));
for (let i = 0; i < limitRulerUnitList.length - 1; i++) {
const thisUnit = limitRulerUnitList[i];
const nextUnit = limitRulerUnitList[i + 1];
if (unit > nextUnit) {
continue;
}
if (unit === thisUnit) {
return unit;
}
if (unit === nextUnit) {
return unit;
}
const mid = (thisUnit + nextUnit) / 2;
if (unit <= mid) {
return thisUnit;
}
return nextUnit;
}
return unit;
}
function calcRulerScaleList(opts: { axis: 'X' | 'Y'; scale: number; viewLength: number; viewOffset: number }): { list: RulerScale[]; rulerUnit: number } {
const { scale, viewLength, viewOffset } = opts;
const list: RulerScale[] = [];
let rulerUnit = 10;
rulerUnit = formatNumber(rulerUnit / scale, { decimalPlaces: 0 });
rulerUnit = Math.max(10, Math.min(rulerUnit, 1000));
// rulerUnit = Math.max(10, Math.min(rulerUnit, 1000));
rulerUnit = limitRulerUnit(rulerUnit);
const rulerKeyUnit = rulerUnit * 10;
const rulerSubKeyUnit = rulerUnit * 5;
@ -46,6 +72,7 @@ function calcRulerScaleList(opts: { axis: 'X' | 'Y'; scale: number; viewLength:
const remainderNum = startNum % viewUnit;
const firstNum = (startNum - remainderNum + viewUnit) / scale;
const firstPosition = startPosition + (viewUnit - remainderNum);
while (firstPosition + index * viewUnit < viewLength) {
const num = formatNumber(firstNum + index * rulerUnit, { decimalPlaces: 0 });
const position = formatNumber(firstPosition + index * viewUnit, { decimalPlaces: 0 });
@ -60,10 +87,10 @@ function calcRulerScaleList(opts: { axis: 'X' | 'Y'; scale: number; viewLength:
index++;
}
return list;
return { list, rulerUnit };
}
export function calcXRulerScaleList(opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): RulerScale[] {
export function calcXRulerScaleList(opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): { list: RulerScale[]; rulerUnit: number } {
const { viewScaleInfo, viewSizeInfo } = opts;
const { scale, offsetLeft } = viewScaleInfo;
const { width } = viewSizeInfo;
@ -75,7 +102,7 @@ export function calcXRulerScaleList(opts: { viewScaleInfo: ViewScaleInfo; viewSi
});
}
export function calcYRulerScaleList(opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): RulerScale[] {
export function calcYRulerScaleList(opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): { list: RulerScale[]; rulerUnit: number } {
const { viewScaleInfo, viewSizeInfo } = opts;
const { scale, offsetTop } = viewScaleInfo;
const { height } = viewSizeInfo;
@ -195,7 +222,7 @@ export function drawRulerBackground(
ctx.stroke();
}
export function drawUnderGrid(
export function drawGrid(
ctx: ViewContext2D,
opts: {
xList: RulerScale[];

View file

@ -6,7 +6,7 @@ import type { DeepScrollerSharedStorage } from './types';
export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage> = (opts) => {
const { viewer, boardContent, sharer } = opts;
const { helperContext } = boardContent;
const { overlayContext } = boardContent;
sharer.setSharedStorage(keyXThumbRect, null); // null | ElementSize
sharer.setSharedStorage(keyYThumbRect, null); // null | ElementSize
@ -48,7 +48,7 @@ export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage> = (o
};
const getThumbType = (p: Point) => {
return isPointInScrollThumb(helperContext, p, {
return isPointInScrollThumb(overlayContext, p, {
xThumbRect: sharer.getSharedStorage(keyXThumbRect),
yThumbRect: sharer.getSharedStorage(keyYThumbRect)
});
@ -97,7 +97,7 @@ export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage> = (o
}
},
beforeDrawFrame({ snapshot }) {
const { xThumbRect, yThumbRect } = drawScroller(helperContext, { snapshot });
const { xThumbRect, yThumbRect } = drawScroller(overlayContext, { snapshot });
sharer.setSharedStorage(keyXThumbRect, xThumbRect);
sharer.setSharedStorage(keyYThumbRect, yThumbRect);
}

View file

@ -15,8 +15,8 @@ const scrollConfig = {
showScrollBar: false
};
function isPointAtRect(helperContext: ViewContext2D, p: Point, rect: ElementSize): boolean {
const ctx = helperContext;
function isPointAtRect(overlayContext: ViewContext2D, p: Point, rect: ElementSize): boolean {
const ctx = overlayContext;
const { x, y, w, h } = rect;
ctx.beginPath();
ctx.rect(x, y, w, h);
@ -28,7 +28,7 @@ function isPointAtRect(helperContext: ViewContext2D, p: Point, rect: ElementSize
}
export function isPointInScrollThumb(
helperContext: ViewContext2D,
overlayContext: ViewContext2D,
p: Point,
opts: {
xThumbRect?: ElementSize | null;
@ -37,9 +37,9 @@ export function isPointInScrollThumb(
): ScrollbarThumbType | null {
let thumbType: ScrollbarThumbType | null = null;
const { xThumbRect, yThumbRect } = opts;
if (xThumbRect && isPointAtRect(helperContext, p, xThumbRect)) {
if (xThumbRect && isPointAtRect(overlayContext, p, xThumbRect)) {
thumbType = 'X';
} else if (yThumbRect && isPointAtRect(helperContext, p, yThumbRect)) {
} else if (yThumbRect && isPointAtRect(overlayContext, p, yThumbRect)) {
thumbType = 'Y';
}
return thumbType;
@ -194,8 +194,8 @@ function drawScrollerThumb(
ctx.restore();
}
function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; scrollInfo: ScrollInfo }) {
const ctx = helperContext;
function drawScrollerInfo(overlayContext: ViewContext2D, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; scrollInfo: ScrollInfo }) {
const ctx = overlayContext;
const { viewScaleInfo, viewSizeInfo, scrollInfo } = opts;
const { activeThumbType, prevPoint, activePoint } = scrollInfo;
const { width, height } = viewSizeInfo;

View file

@ -83,7 +83,7 @@ export { middlewareEventSelect, middlewareEventSelectClear, middlewareEventSelec
export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, CoreEventMap> = (opts) => {
const { viewer, sharer, boardContent, calculator, eventHub } = opts;
const { helperContext } = boardContent;
const { overlayContext } = boardContent;
let prevPoint: Point | null = null;
let inBusyMode: 'resize' | 'drag' | 'drag-list' | 'area' | null = null;
@ -146,7 +146,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
const pointTargetBaseOptions = () => {
return {
ctx: helperContext,
ctx: overlayContext,
calculator,
data: sharer.getActiveStorage('data'),
selectedElements: getActiveElements(),
@ -249,7 +249,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (groupQueue?.length > 0) {
// in group
const isInActiveGroup = isPointInViewActiveGroup(e.point, {
ctx: helperContext,
ctx: overlayContext,
viewScaleInfo: sharer.getActiveViewScaleInfo(),
viewSizeInfo: sharer.getActiveViewSizeInfo(),
groupQueue: sharer.getSharedStorage(keyGroupQueue)
@ -350,7 +350,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (groupQueue?.length > 0) {
if (
isPointInViewActiveGroup(e.point, {
ctx: helperContext,
ctx: overlayContext,
viewScaleInfo: sharer.getActiveViewScaleInfo(),
viewSizeInfo: sharer.getActiveViewSizeInfo(),
groupQueue
@ -750,10 +750,10 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (groupQueue?.length > 0) {
// in group
drawGroupQueueVertexesWrappers(helperContext, groupQueueVertexesList, drawBaseOpts);
drawGroupQueueVertexesWrappers(overlayContext, groupQueueVertexesList, drawBaseOpts);
if (hoverElement && actionType !== 'drag') {
if (isLock) {
drawLockVertexesWrapper(helperContext, hoverElementVertexes, {
drawLockVertexesWrapper(overlayContext, hoverElementVertexes, {
...drawBaseOpts,
controller: calcElementSizeController(hoverElement, {
groupQueue,
@ -762,11 +762,11 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
})
});
} else {
drawHoverVertexesWrapper(helperContext, hoverElementVertexes, drawBaseOpts);
drawHoverVertexesWrapper(overlayContext, hoverElementVertexes, drawBaseOpts);
}
}
if (!isLock && elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
drawSelectedElementControllersVertexes(helperContext, selectedElementController, {
drawSelectedElementControllersVertexes(overlayContext, selectedElementController, {
...drawBaseOpts,
element: elem,
calculator,
@ -775,7 +775,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (actionType === 'drag') {
const xLines = sharer.getSharedStorage(keySelectedReferenceXLines);
const yLines = sharer.getSharedStorage(keySelectedReferenceYLines);
drawReferenceLines(helperContext, {
drawReferenceLines(overlayContext, {
xLines,
yLines
});
@ -785,7 +785,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
// in root
if (hoverElement && actionType !== 'drag') {
if (isLock) {
drawLockVertexesWrapper(helperContext, hoverElementVertexes, {
drawLockVertexesWrapper(overlayContext, hoverElementVertexes, {
...drawBaseOpts,
controller: calcElementSizeController(hoverElement, {
groupQueue,
@ -794,11 +794,11 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
})
});
} else {
drawHoverVertexesWrapper(helperContext, hoverElementVertexes, drawBaseOpts);
drawHoverVertexesWrapper(overlayContext, hoverElementVertexes, drawBaseOpts);
}
}
if (!isLock && elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
drawSelectedElementControllersVertexes(helperContext, selectedElementController, {
drawSelectedElementControllersVertexes(overlayContext, selectedElementController, {
...drawBaseOpts,
element: elem,
calculator,
@ -807,13 +807,13 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (actionType === 'drag') {
const xLines = sharer.getSharedStorage(keySelectedReferenceXLines);
const yLines = sharer.getSharedStorage(keySelectedReferenceYLines);
drawReferenceLines(helperContext, {
drawReferenceLines(overlayContext, {
xLines,
yLines
});
}
} else if (actionType === 'area' && areaStart && areaEnd) {
drawArea(helperContext, { start: areaStart, end: areaEnd });
drawArea(overlayContext, { start: areaStart, end: areaEnd });
} else if ((['drag-list', 'drag-list-end'] as ActionType[]).includes(actionType)) {
const listAreaSize = calcSelectedElementsArea(getActiveElements(), {
viewScaleInfo: sharer.getActiveViewScaleInfo(),
@ -821,7 +821,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
calculator
});
if (listAreaSize) {
drawListArea(helperContext, { areaSize: listAreaSize });
drawListArea(overlayContext, { areaSize: listAreaSize });
}
}
}
@ -834,28 +834,28 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
// const endHorizontal: any = sharer.getSharedStorage(keyDebugEndHorizontal);
// const end0: any = sharer.getSharedStorage(keyDebugEnd0);
// if (elemCenter && end0) {
// helperContext.beginPath();
// helperContext.moveTo(elemCenter.x, elemCenter.y);
// helperContext.lineTo(end0.x, end0.y);
// helperContext.closePath();
// helperContext.strokeStyle = 'black';
// helperContext.stroke();
// overlayContext.beginPath();
// overlayContext.moveTo(elemCenter.x, elemCenter.y);
// overlayContext.lineTo(end0.x, end0.y);
// overlayContext.closePath();
// overlayContext.strokeStyle = 'black';
// overlayContext.stroke();
// }
// if (elemCenter && endVertical) {
// helperContext.beginPath();
// helperContext.moveTo(elemCenter.x, elemCenter.y);
// helperContext.lineTo(endVertical.x, endVertical.y);
// helperContext.closePath();
// helperContext.strokeStyle = 'red';
// helperContext.stroke();
// overlayContext.beginPath();
// overlayContext.moveTo(elemCenter.x, elemCenter.y);
// overlayContext.lineTo(endVertical.x, endVertical.y);
// overlayContext.closePath();
// overlayContext.strokeStyle = 'red';
// overlayContext.stroke();
// }
// if (elemCenter && endHorizontal) {
// helperContext.beginPath();
// helperContext.moveTo(elemCenter.x, elemCenter.y);
// helperContext.lineTo(endHorizontal.x, endHorizontal.y);
// helperContext.closePath();
// helperContext.strokeStyle = 'blue';
// helperContext.stroke();
// overlayContext.beginPath();
// overlayContext.moveTo(elemCenter.x, elemCenter.y);
// overlayContext.lineTo(endHorizontal.x, endHorizontal.y);
// overlayContext.closePath();
// overlayContext.strokeStyle = 'blue';
// overlayContext.stroke();
// }
}
};

View file

@ -1,11 +1,11 @@
import type { Element, RendererDrawElementOptions, ViewContext2D } from '@idraw/types';
import type { Element, RendererDrawElementOptions, ViewContext2D, LoadContent } from '@idraw/types';
import { rotateElement, calcViewBoxSize, calcViewElementSize } from '@idraw/util';
import { drawBox, drawBoxShadow, getOpacity } from './box';
export function drawImage(ctx: ViewContext2D, elem: Element<'image'>, opts: RendererDrawElementOptions) {
const content = opts.loader.getContent(elem);
const content: LoadContent | HTMLCanvasElement | OffscreenCanvas | null = opts.loader.getContent(elem);
const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
const { x, y, w, h, angle } = calcViewElementSize(elem, { viewScaleInfo, viewSizeInfo }) || elem;
const { x, y, w, h, angle } = calcViewElementSize(elem, { viewScaleInfo }) || elem;
const viewElem = { ...elem, ...{ x, y, w, h, angle } };
rotateElement(ctx, { x, y, w, h, angle }, () => {
@ -29,6 +29,10 @@ export function drawImage(ctx: ViewContext2D, elem: Element<'image'>, opts: Rend
viewScaleInfo,
viewSizeInfo
});
const { detail } = elem;
const { scaleMode, originW = 0, originH = 0 } = detail;
const imageW = ctx.$undoPixelRatio(originW);
const imageH = ctx.$undoPixelRatio(originH);
ctx.save();
ctx.fillStyle = 'transparent';
@ -41,7 +45,56 @@ export function drawImage(ctx: ViewContext2D, elem: Element<'image'>, opts: Rend
ctx.closePath();
ctx.fill();
ctx.clip();
ctx.drawImage(content, x, y, w, h);
if (scaleMode && originH && originW) {
let sx = 0;
let sy = 0;
let sWidth = imageW;
let sHeight = imageH;
const dx = x;
const dy = y;
const dWidth = w;
const dHeight = h;
if (imageW > elem.w || imageH > elem.h) {
if (scaleMode === 'fill') {
const sourceScale = Math.max(elem.w / imageW, elem.h / imageH);
const newImageWidth = imageW * sourceScale;
const newImageHeight = imageH * sourceScale;
sx = (newImageWidth - elem.w) / 2 / sourceScale;
sy = (newImageHeight - elem.h) / 2 / sourceScale;
sWidth = elem.w / sourceScale;
sHeight = elem.h / sourceScale;
} else if (scaleMode === 'tile') {
sx = 0;
sy = 0;
sWidth = elem.w;
sHeight = elem.h;
} else if (scaleMode === 'fit') {
const sourceScale = Math.min(elem.w / imageW, elem.h / imageH);
sx = (imageW - elem.w / sourceScale) / 2;
sy = (imageH - elem.h / sourceScale) / 2;
sWidth = elem.w / sourceScale;
sHeight = elem.h / sourceScale;
}
}
ctx.drawImage(content, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
} else {
ctx.drawImage(content, x, y, w, h);
// const sx = 0;
// const sy = 0;
// const sWidth = imageW;
// const sHeight = imageH;
// const dx = x;
// const dy = y;
// const dWidth = w;
// const dHeight = h;
// ctx.drawImage(content, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
}
// content = null;
ctx.globalAlpha = parentOpacity;
ctx.restore();
}

View file

@ -4,7 +4,7 @@ import { drawBox, drawBoxShadow } from './box';
export function drawPath(ctx: ViewContext2D, elem: Element<'path'>, opts: RendererDrawElementOptions) {
const { detail } = elem;
const { originX, originY, originW, originH } = detail;
const { originX, originY, originW, originH, fillRule } = detail;
const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
const { x, y, w, h, angle } = calcViewElementSize(elem, { viewScaleInfo }) || elem;
const scaleW = w / originW;
@ -43,7 +43,7 @@ export function drawPath(ctx: ViewContext2D, elem: Element<'path'>, opts: Render
const path2d = new Path2D(pathStr);
if (detail.fill) {
ctx.fillStyle = detail.fill;
ctx.fill(path2d);
ctx.fill(path2d, fillRule as CanvasFillRule);
}
if (detail.stroke && detail.strokeWidth !== 0) {

View file

@ -121,6 +121,9 @@ export interface ElementHTMLDetail extends ElementBaseDetail {
export interface ElementImageDetail extends ElementBaseDetail {
src: string;
originW?: number;
originH?: number;
scaleMode?: 'auto' | 'fill' | 'fit' | 'tile';
}
export interface ElementSVGDetail extends ElementBaseDetail {
@ -144,6 +147,7 @@ export interface ElementPathDetail extends ElementBaseDetail {
stroke?: string;
strokeWidth?: number;
strokeLineCap?: 'butt' | 'round' | 'square';
fillRule?: string; // "evenodd" | "nonzero"
}
export interface ElementDetailMap {

View file

@ -26,8 +26,8 @@ export interface ViewSizeInfo extends ViewContextSize {
export interface BoardContent {
boardContext: ViewContext2D;
viewContext: ViewContext2D;
helperContext: ViewContext2D;
underContext: ViewContext2D;
overlayContext: ViewContext2D;
underlayContext: ViewContext2D;
drawView: () => void;
}

View file

@ -50,26 +50,26 @@ export function createBoardContent(
if (createCustomContext2D) {
// TODO
const viewContext = createCustomContext2D(ctxOpts);
const helperContext = createCustomContext2D(ctxOpts);
const underContext = createCustomContext2D(ctxOpts);
const overlayContext = createCustomContext2D(ctxOpts);
const underlayContext = createCustomContext2D(ctxOpts);
const boardContext = createContext2D({ ctx, ...ctxOpts });
const drawView = () => {
const { width: w, height: h } = viewContext.$getSize();
boardContext.clearRect(0, 0, w, h);
boardContext.drawImage(underContext.canvas, 0, 0, w, h);
boardContext.drawImage(underlayContext.canvas, 0, 0, w, h);
boardContext.drawImage(viewContext.canvas, 0, 0, w, h);
boardContext.drawImage(helperContext.canvas, 0, 0, w, h);
underContext.clearRect(0, 0, w, h);
boardContext.drawImage(overlayContext.canvas, 0, 0, w, h);
underlayContext.clearRect(0, 0, w, h);
viewContext.clearRect(0, 0, w, h);
helperContext.clearRect(0, 0, w, h);
overlayContext.clearRect(0, 0, w, h);
};
const content: BoardContent = {
underContext,
underlayContext,
viewContext,
helperContext,
overlayContext,
boardContext,
drawView
};
@ -79,26 +79,26 @@ export function createBoardContent(
if (offscreen === true) {
// const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
const viewContext = createOffscreenContext2D(ctxOpts);
const helperContext = createOffscreenContext2D(ctxOpts);
const underContext = createOffscreenContext2D(ctxOpts);
const overlayContext = createOffscreenContext2D(ctxOpts);
const underlayContext = createOffscreenContext2D(ctxOpts);
const boardContext = createContext2D({ ctx, ...ctxOpts });
const drawView = () => {
const { width: w, height: h } = viewContext.$getSize();
boardContext.clearRect(0, 0, w, h);
boardContext.drawImage(underContext.canvas, 0, 0, w, h);
boardContext.drawImage(underlayContext.canvas, 0, 0, w, h);
boardContext.drawImage(viewContext.canvas, 0, 0, w, h);
boardContext.drawImage(helperContext.canvas, 0, 0, w, h);
underContext.clearRect(0, 0, w, h);
boardContext.drawImage(overlayContext.canvas, 0, 0, w, h);
underlayContext.clearRect(0, 0, w, h);
viewContext.clearRect(0, 0, w, h);
helperContext.clearRect(0, 0, w, h);
overlayContext.clearRect(0, 0, w, h);
};
const content: BoardContent = {
underContext,
underlayContext,
viewContext,
helperContext,
overlayContext,
boardContext,
drawView
};
@ -106,24 +106,24 @@ export function createBoardContent(
} else {
// const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
const viewContext = createContext2D(ctxOpts);
const helperContext = createContext2D(ctxOpts);
const underContext = createContext2D(ctxOpts);
const overlayContext = createContext2D(ctxOpts);
const underlayContext = createContext2D(ctxOpts);
const boardContext = createContext2D({ ctx, ...ctxOpts });
const drawView = () => {
boardContext.clearRect(0, 0, width, height);
boardContext.drawImage(underContext.canvas, 0, 0, width, height);
boardContext.drawImage(underlayContext.canvas, 0, 0, width, height);
boardContext.drawImage(viewContext.canvas, 0, 0, width, height);
boardContext.drawImage(helperContext.canvas, 0, 0, width, height);
underContext.clearRect(0, 0, width, height);
boardContext.drawImage(overlayContext.canvas, 0, 0, width, height);
underlayContext.clearRect(0, 0, width, height);
viewContext.clearRect(0, 0, width, height);
helperContext.clearRect(0, 0, width, height);
overlayContext.clearRect(0, 0, width, height);
};
const content: BoardContent = {
underContext,
underlayContext,
viewContext,
helperContext,
overlayContext,
boardContext,
drawView
};