mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 10:08:34 +00:00
feat: implement group element render
This commit is contained in:
parent
d079ea4836
commit
cc0f72c90b
16 changed files with 375 additions and 108 deletions
|
|
@ -148,7 +148,7 @@ export class Calculator implements ViewCalculator {
|
|||
index: -1,
|
||||
element: null
|
||||
};
|
||||
for (let i = 0; i < data.elements.length; i++) {
|
||||
for (let i = data.elements.length - 1; i >= 0; i--) {
|
||||
const elem = data.elements[i];
|
||||
if (this.isPointInElement(p, elem, scaleInfo)) {
|
||||
result.index = i;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import type { AreaSize, ControllerStyle, ElementSizeController } from './types';
|
|||
|
||||
const wrapperColor = '#1973ba';
|
||||
|
||||
export function drawPointWrapper(ctx: ViewContext2D, elem: ElementSize, opts?: Omit<RendererDrawElementOptions, 'loader'>) {
|
||||
export function drawPointWrapper(ctx: ViewContext2D, elem: ElementSize) {
|
||||
const bw = 0;
|
||||
const { x, y, w, h } = elem;
|
||||
const { angle = 0 } = elem;
|
||||
|
|
@ -35,7 +35,7 @@ export function drawPointWrapper(ctx: ViewContext2D, elem: ElementSize, opts?: O
|
|||
});
|
||||
}
|
||||
|
||||
export function drawHoverWrapper(ctx: ViewContext2D, elem: ElementSize, opts?: Omit<RendererDrawElementOptions, 'loader'>) {
|
||||
export function drawHoverWrapper(ctx: ViewContext2D, elem: ElementSize) {
|
||||
const bw = 0;
|
||||
const { x, y, w, h } = elem;
|
||||
const { angle = 0 } = elem;
|
||||
|
|
@ -85,7 +85,7 @@ function drawController(ctx: ViewContext2D, style: ControllerStyle) {
|
|||
export function drawElementControllers(
|
||||
ctx: ViewContext2D,
|
||||
elem: ElementSize,
|
||||
opts: Omit<RendererDrawElementOptions, 'loader'> & { sizeControllers: ElementSizeController }
|
||||
opts: Omit<RendererDrawElementOptions, 'loader' | 'parentElementSize'> & { sizeControllers: ElementSizeController }
|
||||
) {
|
||||
const bw = 0;
|
||||
const { x, y, w, h } = elem;
|
||||
|
|
|
|||
|
|
@ -257,13 +257,13 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => {
|
|||
const drawOpts = { calculator, scaleInfo, viewSize };
|
||||
if (hoverElement && actionType !== 'drag') {
|
||||
const hoverElemSize = calculator.elementSize(hoverElement, scaleInfo);
|
||||
drawHoverWrapper(helperContext, hoverElemSize, drawOpts);
|
||||
drawHoverWrapper(helperContext, hoverElemSize);
|
||||
}
|
||||
|
||||
if (elem && ['select', 'drag', 'resize'].includes(actionType)) {
|
||||
const selectedElemSize = calculator.elementSize(elem, scaleInfo);
|
||||
const sizeControllers = calcElementControllerStyle(selectedElemSize);
|
||||
drawPointWrapper(helperContext, selectedElemSize, drawOpts);
|
||||
drawPointWrapper(helperContext, selectedElemSize);
|
||||
drawElementControllers(helperContext, selectedElemSize, { ...drawOpts, sizeControllers });
|
||||
} else if (actionType === 'area' && areaStart && areaEnd) {
|
||||
drawArea(helperContext, { start: areaStart, end: areaEnd });
|
||||
|
|
|
|||
|
|
@ -432,6 +432,7 @@ export function calcSelectedElementsArea(
|
|||
|
||||
if (elemSize.angle && (elemSize.angle > 0 || elemSize.angle < 0)) {
|
||||
const ves = rotateElementVertexes(elemSize);
|
||||
|
||||
if (ves.length === 4) {
|
||||
const xList = [ves[0].x, ves[1].x, ves[2].x, ves[3].x];
|
||||
const yList = [ves[0].y, ves[1].y, ves[2].y, ves[3].y];
|
||||
|
|
|
|||
|
|
@ -145,7 +145,6 @@ const data: Data = {
|
|||
}
|
||||
},
|
||||
{
|
||||
name: 'html-001',
|
||||
uuid: 'xxx-0012',
|
||||
x: 400,
|
||||
y: 200,
|
||||
|
|
@ -196,11 +195,160 @@ const data: Data = {
|
|||
<div class="btn-box">
|
||||
<button class="btn btn-primary">
|
||||
<span>Button Primary</span>
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001',
|
||||
x: 400,
|
||||
y: 400,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'group',
|
||||
desc: {
|
||||
bgColor: '#1f1f1f',
|
||||
children: [
|
||||
{
|
||||
uuid: 'group-001-0014',
|
||||
type: 'circle',
|
||||
x: -40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#f44336'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001-0015',
|
||||
type: 'circle',
|
||||
x: -20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ff9800'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001-0016',
|
||||
type: 'circle',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ffc106'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001-0017',
|
||||
type: 'circle',
|
||||
x: 20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#cddc39'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001-0018',
|
||||
type: 'circle',
|
||||
x: 40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#4caf50'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-0013',
|
||||
x: 550,
|
||||
y: 100,
|
||||
w: 173.20508075688775,
|
||||
// w: 100,
|
||||
h: 100,
|
||||
angle: 30,
|
||||
type: 'group',
|
||||
desc: {
|
||||
children: [
|
||||
{
|
||||
uuid: 'group-002-014',
|
||||
type: 'circle',
|
||||
x: -40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#f44336'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-002-0015',
|
||||
type: 'circle',
|
||||
x: -20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ff9800'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-002-0016',
|
||||
type: 'circle',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ffc106'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-002-0017',
|
||||
type: 'circle',
|
||||
x: 20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#cddc39'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-002-0018',
|
||||
type: 'circle',
|
||||
x: 40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#4caf50'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0017',
|
||||
type: 'image',
|
||||
x: 100,
|
||||
y: 400,
|
||||
w: 100,
|
||||
h: 100,
|
||||
angle: 30,
|
||||
desc: {
|
||||
src: './images/lena.png?v=0017'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,87 +1,81 @@
|
|||
import { ViewContext2D, Element } from '@idraw/types';
|
||||
import { is, istype, isColorStr, rotateElement } from '@idraw/util';
|
||||
import { ViewContext2D, Element, ElementType } from '@idraw/types';
|
||||
import { is, istype, isColorStr } from '@idraw/util';
|
||||
|
||||
export function clearContext(ctx: ViewContext2D) {
|
||||
ctx.fillStyle = '#000000';
|
||||
ctx.strokeStyle = '#000000';
|
||||
ctx.setLineDash([]);
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.shadowColor = '#00000000';
|
||||
ctx.shadowOffsetX = 0;
|
||||
ctx.shadowOffsetY = 0;
|
||||
ctx.shadowBlur = 0;
|
||||
}
|
||||
// export function clearContext(ctx: ViewContext2D) {
|
||||
// ctx.fillStyle = '#000000';
|
||||
// ctx.strokeStyle = '#000000';
|
||||
// ctx.setLineDash([]);
|
||||
// ctx.globalAlpha = 1;
|
||||
// ctx.shadowColor = '#00000000';
|
||||
// ctx.shadowOffsetX = 0;
|
||||
// ctx.shadowOffsetY = 0;
|
||||
// ctx.shadowBlur = 0;
|
||||
// }
|
||||
|
||||
export function drawBox(ctx: ViewContext2D, elem: Element<'text' | 'rect'>, pattern: string | CanvasPattern | null): void {
|
||||
clearContext(ctx);
|
||||
export function drawBox(ctx: ViewContext2D, elem: Element<ElementType>, pattern?: string | CanvasPattern | null): void {
|
||||
drawBoxBorder(ctx, elem);
|
||||
clearContext(ctx);
|
||||
rotateElement(ctx, elem, () => {
|
||||
const { x, y, w, h } = elem;
|
||||
let r: number = elem.desc.borderRadius || 0;
|
||||
r = Math.min(r, w / 2, h / 2);
|
||||
if (w < r * 2 || h < r * 2) {
|
||||
r = 0;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, r);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, r);
|
||||
ctx.arcTo(x, y + h, x, y, r);
|
||||
ctx.arcTo(x, y, x + w, y, r);
|
||||
ctx.closePath();
|
||||
if (typeof pattern === 'string') {
|
||||
ctx.fillStyle = pattern;
|
||||
} else if (['CanvasPattern'].includes(istype.type(pattern))) {
|
||||
ctx.fillStyle = pattern as CanvasPattern;
|
||||
}
|
||||
ctx.fill();
|
||||
});
|
||||
|
||||
const { x, y, w, h } = elem;
|
||||
let r: number = elem.desc.borderRadius || 0;
|
||||
r = Math.min(r, w / 2, h / 2);
|
||||
if (w < r * 2 || h < r * 2) {
|
||||
r = 0;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, r);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, r);
|
||||
ctx.arcTo(x, y + h, x, y, r);
|
||||
ctx.arcTo(x, y, x + w, y, r);
|
||||
ctx.closePath();
|
||||
if (typeof pattern === 'string') {
|
||||
ctx.fillStyle = pattern;
|
||||
} else if (['CanvasPattern'].includes(istype.type(pattern))) {
|
||||
ctx.fillStyle = pattern as CanvasPattern;
|
||||
}
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
export function drawBoxBorder(ctx: ViewContext2D, elem: Element<'text' | 'rect'>): void {
|
||||
clearContext(ctx);
|
||||
rotateElement(ctx, elem, () => {
|
||||
if (!(elem.desc.borderWidth && elem.desc.borderWidth > 0)) {
|
||||
return;
|
||||
}
|
||||
const bw = elem.desc.borderWidth;
|
||||
let borderColor = '#000000';
|
||||
if (isColorStr(elem.desc.borderColor) === true) {
|
||||
borderColor = elem.desc.borderColor as string;
|
||||
}
|
||||
const x = elem.x - bw / 2;
|
||||
const y = elem.y - bw / 2;
|
||||
const w = elem.w + bw;
|
||||
const h = elem.h + bw;
|
||||
export function drawBoxBorder(ctx: ViewContext2D, elem: Element<ElementType>): void {
|
||||
if (!(elem.desc.borderWidth && elem.desc.borderWidth > 0)) {
|
||||
return;
|
||||
}
|
||||
const bw = elem.desc.borderWidth;
|
||||
let borderColor = '#000000';
|
||||
if (isColorStr(elem.desc.borderColor) === true) {
|
||||
borderColor = elem.desc.borderColor as string;
|
||||
}
|
||||
const x = elem.x - bw / 2;
|
||||
const y = elem.y - bw / 2;
|
||||
const w = elem.w + bw;
|
||||
const h = elem.h + bw;
|
||||
|
||||
let r: number = elem.desc.borderRadius || 0;
|
||||
r = Math.min(r, w / 2, h / 2);
|
||||
if (r < w / 2 && r < h / 2) {
|
||||
r = r + bw / 2;
|
||||
}
|
||||
const { desc } = elem;
|
||||
if (desc.shadowColor !== undefined && isColorStr(desc.shadowColor)) {
|
||||
ctx.shadowColor = desc.shadowColor;
|
||||
}
|
||||
if (desc.shadowOffsetX !== undefined && is.number(desc.shadowOffsetX)) {
|
||||
ctx.shadowOffsetX = desc.shadowOffsetX;
|
||||
}
|
||||
if (desc.shadowOffsetY !== undefined && is.number(desc.shadowOffsetY)) {
|
||||
ctx.shadowOffsetY = desc.shadowOffsetY;
|
||||
}
|
||||
if (desc.shadowBlur !== undefined && is.number(desc.shadowBlur)) {
|
||||
ctx.shadowBlur = desc.shadowBlur;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = bw;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, r);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, r);
|
||||
ctx.arcTo(x, y + h, x, y, r);
|
||||
ctx.arcTo(x, y, x + w, y, r);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
});
|
||||
let r: number = elem.desc.borderRadius || 0;
|
||||
r = Math.min(r, w / 2, h / 2);
|
||||
if (r < w / 2 && r < h / 2) {
|
||||
r = r + bw / 2;
|
||||
}
|
||||
const { desc } = elem;
|
||||
if (desc.shadowColor !== undefined && isColorStr(desc.shadowColor)) {
|
||||
ctx.shadowColor = desc.shadowColor;
|
||||
}
|
||||
if (desc.shadowOffsetX !== undefined && is.number(desc.shadowOffsetX)) {
|
||||
ctx.shadowOffsetX = desc.shadowOffsetX;
|
||||
}
|
||||
if (desc.shadowOffsetY !== undefined && is.number(desc.shadowOffsetY)) {
|
||||
ctx.shadowOffsetY = desc.shadowOffsetY;
|
||||
}
|
||||
if (desc.shadowBlur !== undefined && is.number(desc.shadowBlur)) {
|
||||
ctx.shadowBlur = desc.shadowBlur;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = bw;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.moveTo(x + r, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, r);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, r);
|
||||
ctx.arcTo(x, y + h, x, y, r);
|
||||
ctx.arcTo(x, y, x + w, y, r);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { drawImage } from './image';
|
|||
import { drawText } from './text';
|
||||
import { drawSVG } from './svg';
|
||||
import { drawHTML } from './html';
|
||||
import { drawGroup } from './group';
|
||||
|
||||
export function drawElement(ctx: ViewContext2D, elem: Element<ElementType>, opts: RendererDrawElementOptions) {
|
||||
try {
|
||||
|
|
@ -33,6 +34,10 @@ export function drawElement(ctx: ViewContext2D, elem: Element<ElementType>, opts
|
|||
drawHTML(ctx, elem as Element<'html'>, opts);
|
||||
break;
|
||||
}
|
||||
case 'group': {
|
||||
drawGroup(ctx, elem as Element<'group'>, opts);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
|
|
@ -43,7 +48,7 @@ export function drawElement(ctx: ViewContext2D, elem: Element<ElementType>, opts
|
|||
}
|
||||
|
||||
export function drawElementList(ctx: ViewContext2D, elements: Data['elements'], opts: RendererDrawElementOptions) {
|
||||
for (let i = elements.length - 1; i >= 0; i--) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const elem = elements[i];
|
||||
if (!opts.calculator.isElementInView(elem, opts.scaleInfo, opts.viewSize)) {
|
||||
continue;
|
||||
|
|
|
|||
95
packages/renderer/src/draw/group.ts
Normal file
95
packages/renderer/src/draw/group.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import type { Element, ElementType, ElementSize, RendererDrawElementOptions, ViewContext2D } from '@idraw/types';
|
||||
import { rotateElement } from '@idraw/util';
|
||||
import { drawCircle } from './circle';
|
||||
import { drawRect } from './rect';
|
||||
import { drawImage } from './image';
|
||||
import { drawText } from './text';
|
||||
import { drawSVG } from './svg';
|
||||
import { drawHTML } from './html';
|
||||
import { drawBox } from './base';
|
||||
|
||||
export function drawElement(ctx: ViewContext2D, elem: Element<ElementType>, opts: RendererDrawElementOptions) {
|
||||
try {
|
||||
switch (elem.type) {
|
||||
case 'rect': {
|
||||
drawRect(ctx, elem as Element<'rect'>, opts);
|
||||
break;
|
||||
}
|
||||
case 'circle': {
|
||||
drawCircle(ctx, elem as Element<'circle'>, opts);
|
||||
break;
|
||||
}
|
||||
case 'text': {
|
||||
drawText(ctx, elem as Element<'text'>, opts);
|
||||
break;
|
||||
}
|
||||
case 'image': {
|
||||
drawImage(ctx, elem as Element<'image'>, opts);
|
||||
break;
|
||||
}
|
||||
case 'svg': {
|
||||
drawSVG(ctx, elem as Element<'svg'>, opts);
|
||||
break;
|
||||
}
|
||||
case 'html': {
|
||||
drawHTML(ctx, elem as Element<'html'>, opts);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
export function drawGroup(ctx: ViewContext2D, elem: Element<'group'>, opts: RendererDrawElementOptions) {
|
||||
const { calculator, scaleInfo } = opts;
|
||||
const { x, y, w, h, angle } = calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h, angle: elem.angle }, scaleInfo);
|
||||
|
||||
rotateElement(ctx, { x, y, w, h, angle }, () => {
|
||||
drawBox(ctx, elem);
|
||||
if (Array.isArray(elem.desc.children)) {
|
||||
const { parentElementSize: parentSize } = opts;
|
||||
const newParentSize: ElementSize = {
|
||||
x: parentSize.x + elem.x,
|
||||
y: parentSize.y + elem.y,
|
||||
w: elem.w || parentSize.w,
|
||||
h: elem.h || parentSize.h,
|
||||
angle: elem.angle
|
||||
};
|
||||
const { calculator } = opts;
|
||||
ctx.save();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y);
|
||||
ctx.lineTo(x + w, y + h);
|
||||
ctx.lineTo(x, y + h);
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
|
||||
for (let i = 0; i < elem.desc.children.length; i++) {
|
||||
let child = elem.desc.children[i];
|
||||
child = {
|
||||
...child,
|
||||
...{
|
||||
x: newParentSize.x + child.x,
|
||||
y: newParentSize.y + child.y
|
||||
}
|
||||
};
|
||||
if (!calculator.isElementInView(child, opts.scaleInfo, opts.viewSize)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
drawElement(ctx, child, opts);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ export function drawRect(ctx: ViewContext2D, elem: Element<'rect'>, opts: Render
|
|||
// const { desc } = elem;
|
||||
const { calculator, scaleInfo } = opts;
|
||||
|
||||
const { x, y, w, h, angle } = calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h }, scaleInfo);
|
||||
const { x, y, w, h, angle } = calculator.elementSize(elem, scaleInfo);
|
||||
rotateElement(ctx, { x, y, w, h, angle }, () => {
|
||||
let r: number = (elem.desc.borderRadius || 0) * scaleInfo.scale;
|
||||
r = Math.min(r, w / 2, h / 2);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import type { Element, RendererDrawElementOptions, ViewContext2D } from '@idraw/types';
|
||||
import { rotateElement } from '@idraw/util';
|
||||
import { is, isColorStr } from '@idraw/util';
|
||||
import { clearContext, drawBox } from './base';
|
||||
import { drawBox } from './base';
|
||||
|
||||
export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: RendererDrawElementOptions) {
|
||||
clearContext(ctx);
|
||||
drawBox(ctx, elem, elem.desc.bgColor || 'transparent');
|
||||
rotateElement(ctx, elem, () => {
|
||||
drawBox(ctx, elem, elem.desc.bgColor || 'transparent');
|
||||
const desc: Element<'text'>['desc'] = {
|
||||
...{
|
||||
fontSize: 12,
|
||||
|
|
@ -103,7 +102,6 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render
|
|||
}
|
||||
ctx.fillText(line.text, _x, _y + fontHeight * i);
|
||||
});
|
||||
clearContext(ctx);
|
||||
}
|
||||
|
||||
// draw text stroke
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EventEmitter, createOffscreenContext2D } from '@idraw/util';
|
||||
import { EventEmitter } from '@idraw/util';
|
||||
import { drawElementList } from './draw';
|
||||
import { Loader } from './loader';
|
||||
import type { Data, BoardRenderer, RendererOptions, RendererEventMap, RendererDrawOptions } from '@idraw/types';
|
||||
|
|
@ -6,17 +6,17 @@ import type { Data, BoardRenderer, RendererOptions, RendererEventMap, RendererDr
|
|||
export class Renderer extends EventEmitter<RendererEventMap> implements BoardRenderer {
|
||||
private _opts: RendererOptions;
|
||||
private _loader: Loader = new Loader();
|
||||
private _draftContextTop: CanvasRenderingContext2D;
|
||||
private _draftContextMiddle: CanvasRenderingContext2D;
|
||||
private _draftContextBottom: CanvasRenderingContext2D;
|
||||
// private _draftContextTop: CanvasRenderingContext2D;
|
||||
// private _draftContextMiddle: CanvasRenderingContext2D;
|
||||
// private _draftContextBottom: CanvasRenderingContext2D;
|
||||
|
||||
constructor(opts: RendererOptions) {
|
||||
super();
|
||||
this._opts = opts;
|
||||
const { width, height } = this._opts.viewContent.viewContext.canvas;
|
||||
this._draftContextTop = createOffscreenContext2D({ width, height }) as CanvasRenderingContext2D;
|
||||
this._draftContextMiddle = createOffscreenContext2D({ width, height }) as CanvasRenderingContext2D;
|
||||
this._draftContextBottom = createOffscreenContext2D({ width, height }) as CanvasRenderingContext2D;
|
||||
// const { width, height } = this._opts.viewContent.viewContext.canvas;
|
||||
// this._draftContextTop = createOffscreenContext2D({ width, height }) as CanvasRenderingContext2D;
|
||||
// this._draftContextMiddle = createOffscreenContext2D({ width, height }) as CanvasRenderingContext2D;
|
||||
// this._draftContextBottom = createOffscreenContext2D({ width, height }) as CanvasRenderingContext2D;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
|
@ -40,7 +40,18 @@ export class Renderer extends EventEmitter<RendererEventMap> implements BoardRen
|
|||
const { calculator } = this._opts;
|
||||
const { viewContext } = this._opts.viewContent;
|
||||
viewContext.clearRect(0, 0, viewContext.canvas.width, viewContext.canvas.height);
|
||||
drawElementList(viewContext, data.elements, { loader, calculator, ...opts });
|
||||
const parentElementSize = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: opts.viewSize.width,
|
||||
h: opts.viewSize.height
|
||||
};
|
||||
drawElementList(viewContext, data.elements, {
|
||||
loader,
|
||||
calculator,
|
||||
parentElementSize,
|
||||
...opts
|
||||
});
|
||||
}
|
||||
|
||||
scale(num: number) {
|
||||
|
|
|
|||
|
|
@ -54,4 +54,6 @@ export interface ViewContext2D {
|
|||
shadowOffsetY: number;
|
||||
ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
|
||||
isPointInPath(x: number, y: number, fillRule?: CanvasFillRule): boolean;
|
||||
clip(fillRule?: CanvasFillRule): void;
|
||||
clip(path: Path2D, fillRule?: CanvasFillRule): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ interface ElemenTextDesc extends ElementBaseDesc {
|
|||
color: string;
|
||||
fontSize: number;
|
||||
lineHeight?: number;
|
||||
fontWeight?: 'bold' | '';
|
||||
fontWeight?: 'bold' | string;
|
||||
fontFamily?: string;
|
||||
textAlign?: 'center' | 'left' | 'right';
|
||||
verticalAlign?: 'middle' | 'top' | 'bottom';
|
||||
|
|
@ -58,6 +58,10 @@ interface ElementSVGDesc extends ElementBaseDesc {
|
|||
svg: string;
|
||||
}
|
||||
|
||||
interface ElementGroupDesc extends ElementBaseDesc {
|
||||
children: Element<ElementType>[];
|
||||
}
|
||||
|
||||
interface ElementDescMap {
|
||||
rect: ElementRectDesc;
|
||||
circle: ElementCircleDesc;
|
||||
|
|
@ -65,9 +69,10 @@ interface ElementDescMap {
|
|||
image: ElementImageDesc;
|
||||
html: ElementHTMLDesc;
|
||||
svg: ElementSVGDesc;
|
||||
group: ElementGroupDesc;
|
||||
}
|
||||
|
||||
export type ElementType = 'text' | 'rect' | 'circle' | 'image' | 'svg' | 'html';
|
||||
export type ElementType = 'text' | 'rect' | 'circle' | 'image' | 'svg' | 'html' | 'group';
|
||||
|
||||
export interface ElementOperation {
|
||||
lock?: boolean;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { ViewContent, ViewScaleInfo, ViewCalculator, ViewSizeInfo } from './view';
|
||||
import type { Element } from './element';
|
||||
import type { Element, ElementSize } from './element';
|
||||
import type { LoaderEventMap, LoadElementType, LoadContent } from './loader';
|
||||
import type { UtilEventEmitter } from './util';
|
||||
import type { StoreSharer } from './store';
|
||||
|
|
@ -31,4 +31,6 @@ export interface RendererDrawOptions {
|
|||
export interface RendererDrawElementOptions extends RendererDrawOptions {
|
||||
loader: RendererLoader;
|
||||
calculator: ViewCalculator;
|
||||
scaleInfo: ViewScaleInfo;
|
||||
parentElementSize: ElementSize;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,4 +272,10 @@ export class Context2D implements ViewContext2D {
|
|||
isPointInPath(x: number, y: number) {
|
||||
return this._ctx.isPointInPath(this.$doPixelRatio(x), this.$doPixelRatio(y));
|
||||
}
|
||||
|
||||
// clip(fillRule?: CanvasFillRule): void;
|
||||
// clip(path: Path2D, fillRule?: CanvasFillRule): void;
|
||||
clip(...args: [fillRule?: CanvasFillRule | undefined] | [path: Path2D, fillRule?: CanvasFillRule | undefined]) {
|
||||
return this._ctx.clip(...(args as any[]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,9 +85,9 @@ function calcLineRadian(center: PointSize, p: PointSize): number {
|
|||
} else if (x > 0 && y > 0) {
|
||||
return Math.PI - Math.atan(Math.abs(x) / Math.abs(y));
|
||||
} else if (x < 0 && y > 0) {
|
||||
return Math.PI + Math.atan(Math.abs(y) / Math.abs(x));
|
||||
return Math.PI + Math.atan(Math.abs(x) / Math.abs(y));
|
||||
} else if (x < 0 && y < 0) {
|
||||
return 2 * Math.PI - Math.atan(Math.abs(y) / Math.abs(x));
|
||||
return 2 * Math.PI - Math.atan(Math.abs(x) / Math.abs(y));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue