mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 01:58:27 +00:00
feat: improve box render
This commit is contained in:
parent
a7da9c4bd0
commit
3f9dca1353
9 changed files with 306 additions and 98 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { ViewContext2D, Element, ElementType, ElementSize, ViewScaleInfo, ViewSizeInfo, TransformAction } from '@idraw/types';
|
||||
import { istype, isColorStr, generateSVGPath, rotateElement, is } from '@idraw/util';
|
||||
import { createColorStyle } from './color';
|
||||
|
||||
export function drawBox(
|
||||
ctx: ViewContext2D,
|
||||
|
|
@ -26,9 +27,11 @@ export function drawBox(
|
|||
viewScaleInfo,
|
||||
viewSizeInfo,
|
||||
renderContent: () => {
|
||||
drawBoxBorder(ctx, viewElem, { viewScaleInfo, viewSizeInfo });
|
||||
drawBoxBackground(ctx, viewElem, { pattern, viewScaleInfo, viewSizeInfo });
|
||||
renderContent?.();
|
||||
drawBoxBorder(ctx, viewElem, { viewScaleInfo, viewSizeInfo });
|
||||
// TODO
|
||||
// drawBoxBackground(ctx, viewElem, { pattern, viewScaleInfo, viewSizeInfo });
|
||||
}
|
||||
});
|
||||
ctx.globalAlpha = 1;
|
||||
|
|
@ -84,20 +87,53 @@ function drawBoxBackground(
|
|||
opts: { pattern?: string | CanvasPattern | null; viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }
|
||||
): void {
|
||||
const { pattern, viewScaleInfo } = opts;
|
||||
const { scale } = viewScaleInfo;
|
||||
let transform: TransformAction[] = [];
|
||||
let { borderRadius, boxSizing, borderWidth } = viewElem.detail;
|
||||
if (typeof borderWidth !== 'number') {
|
||||
// TODO: If borderWidth is an array, borderRadius will not take effect and will become 0.
|
||||
borderRadius = 0;
|
||||
}
|
||||
if (viewElem.detail.background || pattern) {
|
||||
const { x, y, w, h } = viewElem;
|
||||
let r: number = (viewElem.detail.borderRadius || 0) * viewScaleInfo.scale;
|
||||
r = Math.min(r, w / 2, h / 2);
|
||||
if (w < r * 2 || h < r * 2) {
|
||||
r = 0;
|
||||
let { x, y, w, h } = viewElem;
|
||||
let radiusList: [number, number, number, number] = [0, 0, 0, 0];
|
||||
if (typeof borderRadius === 'number') {
|
||||
const br = borderRadius * scale;
|
||||
radiusList = [br, br, br, br];
|
||||
} else if (Array.isArray(borderRadius) && borderRadius?.length === 4) {
|
||||
radiusList = [borderRadius[0] * scale, borderRadius[1] * scale, borderRadius[2] * scale, borderRadius[3] * scale];
|
||||
}
|
||||
let bw: number = 0;
|
||||
if (typeof borderWidth === 'number') {
|
||||
bw = (borderWidth || 1) * scale;
|
||||
}
|
||||
if (boxSizing === 'border-box') {
|
||||
x = viewElem.x + bw / 2;
|
||||
y = viewElem.y + bw / 2;
|
||||
w = viewElem.w - bw;
|
||||
h = viewElem.h - bw;
|
||||
} else if (boxSizing === 'content-box') {
|
||||
x = viewElem.x - bw / 2;
|
||||
y = viewElem.y - bw / 2;
|
||||
w = viewElem.w + bw;
|
||||
h = viewElem.h + bw;
|
||||
} else {
|
||||
x = viewElem.x;
|
||||
y = viewElem.y;
|
||||
w = viewElem.w;
|
||||
h = viewElem.h;
|
||||
}
|
||||
|
||||
// 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.moveTo(x + radiusList[0], y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, radiusList[1]);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, radiusList[2]);
|
||||
ctx.arcTo(x, y + h, x, y, radiusList[3]);
|
||||
ctx.arcTo(x, y, x + w, y, radiusList[0]);
|
||||
ctx.closePath();
|
||||
if (typeof pattern === 'string') {
|
||||
ctx.fillStyle = pattern;
|
||||
|
|
@ -106,38 +142,17 @@ function drawBoxBackground(
|
|||
} else if (typeof viewElem.detail.background === 'string') {
|
||||
ctx.fillStyle = viewElem.detail.background;
|
||||
} else if (viewElem.detail.background?.type === 'linearGradient') {
|
||||
const { start, end, stops } = viewElem.detail.background;
|
||||
const viewStart = {
|
||||
x: start.x + x,
|
||||
y: start.y + y
|
||||
};
|
||||
const viewEnd = {
|
||||
x: end.x + x,
|
||||
y: end.y + y
|
||||
};
|
||||
const linearGradient = ctx.createLinearGradient(viewStart.x, viewStart.y, viewEnd.x, viewEnd.y);
|
||||
stops.forEach((stop) => {
|
||||
linearGradient.addColorStop(stop.offset, stop.color);
|
||||
const colorStyle = createColorStyle(ctx, viewElem.detail.background, {
|
||||
viewElementSize: { x, y, w, h },
|
||||
viewScaleInfo
|
||||
});
|
||||
ctx.fillStyle = linearGradient;
|
||||
ctx.fillStyle = colorStyle;
|
||||
} else if (viewElem.detail.background?.type === 'radialGradient') {
|
||||
const { inner, outer, stops } = viewElem.detail.background;
|
||||
transform = viewElem.detail.background.transform || [];
|
||||
const viewInner = {
|
||||
x: inner.x,
|
||||
y: inner.y,
|
||||
radius: inner.radius * viewScaleInfo.scale
|
||||
};
|
||||
const viewOuter = {
|
||||
x: outer.x,
|
||||
y: outer.y,
|
||||
radius: outer.radius * viewScaleInfo.scale
|
||||
};
|
||||
const radialGradient = ctx.createRadialGradient(viewInner.x, viewInner.y, viewInner.radius, viewOuter.x, viewOuter.y, viewOuter.radius);
|
||||
stops.forEach((stop) => {
|
||||
radialGradient.addColorStop(stop.offset, stop.color);
|
||||
const colorStyle = createColorStyle(ctx, viewElem.detail.background, {
|
||||
viewElementSize: { x, y, w, h },
|
||||
viewScaleInfo
|
||||
});
|
||||
ctx.fillStyle = radialGradient;
|
||||
ctx.fillStyle = colorStyle;
|
||||
if (transform && transform.length > 0) {
|
||||
for (let i = 0; i < transform?.length; i++) {
|
||||
const action = transform[i];
|
||||
|
|
@ -176,17 +191,24 @@ function drawBoxBorder(ctx: ViewContext2D, viewElem: Element<ElementType>, opts:
|
|||
return;
|
||||
}
|
||||
const { viewScaleInfo } = opts;
|
||||
const { scale } = viewScaleInfo;
|
||||
let borderColor = '#000000';
|
||||
if (isColorStr(viewElem.detail.borderColor) === true) {
|
||||
borderColor = viewElem.detail.borderColor as string;
|
||||
}
|
||||
const { borderWidth, borderRadius, borderDash } = viewElem.detail;
|
||||
const { borderWidth, borderRadius, borderDash, boxSizing } = viewElem.detail;
|
||||
let bw: number = 0;
|
||||
if (typeof borderWidth === 'number') {
|
||||
bw = borderWidth || 1;
|
||||
}
|
||||
bw = bw * viewScaleInfo.scale;
|
||||
let r: number = borderRadius || 0;
|
||||
bw = bw * scale;
|
||||
let radiusList: [number, number, number, number] = [0, 0, 0, 0];
|
||||
if (typeof borderRadius === 'number') {
|
||||
const br = borderRadius * scale;
|
||||
radiusList = [br, br, br, br];
|
||||
} else if (Array.isArray(borderRadius) && borderRadius?.length === 4) {
|
||||
radiusList = [borderRadius[0] * scale, borderRadius[1] * scale, borderRadius[2] * scale, borderRadius[3] * scale];
|
||||
}
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.setLineDash(borderDash || []);
|
||||
|
||||
|
|
@ -195,72 +217,96 @@ function drawBoxBorder(ctx: ViewContext2D, viewElem: Element<ElementType>, opts:
|
|||
let borderBottom = 0;
|
||||
let borderLeft = 0;
|
||||
if (Array.isArray(borderWidth)) {
|
||||
borderTop = borderWidth[0] || 0;
|
||||
borderRight = borderWidth[1] || 0;
|
||||
borderBottom = borderWidth[2] || 0;
|
||||
borderLeft = borderWidth[3] || 0;
|
||||
borderTop = (borderWidth[0] || 0) * scale;
|
||||
borderRight = (borderWidth[1] || 0) * scale;
|
||||
borderBottom = (borderWidth[2] || 0) * scale;
|
||||
borderLeft = (borderWidth[3] || 0) * scale;
|
||||
}
|
||||
if (borderLeft || borderRight || borderTop || borderBottom) {
|
||||
const { x, y, w, h } = viewElem;
|
||||
if (borderLeft) {
|
||||
let { x, y, w, h } = viewElem;
|
||||
if (boxSizing === 'border-box') {
|
||||
x = x + borderLeft / 2;
|
||||
y = y + borderTop / 2;
|
||||
w = w - borderLeft / 2 - borderRight / 2;
|
||||
h = h - borderTop / 2 - borderBottom / 2;
|
||||
} else if (boxSizing === 'content-box') {
|
||||
x = x - borderLeft / 2;
|
||||
y = y - borderTop / 2;
|
||||
w = w + borderLeft / 2 + borderRight / 2;
|
||||
h = h + borderTop / 2 + borderBottom / 2;
|
||||
} else {
|
||||
// center-line
|
||||
x = viewElem.x;
|
||||
y = viewElem.y;
|
||||
w = viewElem.w;
|
||||
h = viewElem.h;
|
||||
}
|
||||
|
||||
if (borderTop) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = borderLeft * viewScaleInfo.scale;
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x, y + h);
|
||||
ctx.lineWidth = borderTop;
|
||||
ctx.moveTo(x - borderLeft / 2, y);
|
||||
ctx.lineTo(x + w + borderRight / 2, y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
if (borderRight) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = borderRight * viewScaleInfo.scale;
|
||||
ctx.moveTo(x + w, y);
|
||||
ctx.lineTo(x + w, y + h);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
if (borderTop) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = borderTop * viewScaleInfo.scale;
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y);
|
||||
ctx.lineWidth = borderRight;
|
||||
ctx.moveTo(x + w, y - borderTop / 2);
|
||||
ctx.lineTo(x + w, y + h + borderBottom / 2);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
if (borderBottom) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = borderBottom * viewScaleInfo.scale;
|
||||
ctx.moveTo(x, y + h);
|
||||
ctx.lineTo(x + w, y + h);
|
||||
ctx.lineWidth = borderBottom;
|
||||
ctx.moveTo(x - borderLeft / 2, y + h);
|
||||
ctx.lineTo(x + w + borderRight / 2, y + h);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
if (borderLeft) {
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = borderLeft;
|
||||
ctx.moveTo(x, y - borderTop / 2);
|
||||
ctx.lineTo(x, y + h + borderBottom / 2);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
} else {
|
||||
let { x, y, w, h } = viewElem;
|
||||
const { boxSizing } = viewElem.detail;
|
||||
|
||||
if (boxSizing === 'border-box') {
|
||||
x = viewElem.x + bw / 2;
|
||||
y = viewElem.y + bw / 2;
|
||||
w = viewElem.w - bw;
|
||||
h = viewElem.h - bw;
|
||||
} else if (boxSizing === 'content-box') {
|
||||
x = viewElem.x - bw / 2;
|
||||
y = viewElem.y - bw / 2;
|
||||
w = viewElem.w + bw;
|
||||
h = viewElem.h + bw;
|
||||
} else {
|
||||
// center-line
|
||||
x = viewElem.x;
|
||||
y = viewElem.y;
|
||||
w = viewElem.w;
|
||||
h = viewElem.h;
|
||||
} else {
|
||||
x = viewElem.x - bw;
|
||||
y = viewElem.y - bw;
|
||||
w = viewElem.w + bw * 2;
|
||||
h = viewElem.h + bw * 2;
|
||||
}
|
||||
|
||||
r = Math.min(r, w / 2, h / 2);
|
||||
if (r < w / 2 && r < h / 2) {
|
||||
r = r + bw / 2;
|
||||
}
|
||||
// r = Math.min(r, w / 2, h / 2);
|
||||
// if (r < w / 2 && r < h / 2) {
|
||||
// r = r + bw / 2;
|
||||
// }
|
||||
ctx.beginPath();
|
||||
ctx.lineCap = 'square';
|
||||
ctx.lineWidth = bw;
|
||||
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.moveTo(x + radiusList[0], y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, radiusList[1]);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, radiusList[2]);
|
||||
ctx.arcTo(x, y + h, x, y, radiusList[3]);
|
||||
ctx.arcTo(x, y, x + w, y, radiusList[0]);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { Element, RendererDrawElementOptions, ViewContext2D } from '@idraw/types';
|
||||
import { rotateElement } from '@idraw/util';
|
||||
import { createColorStyle } from './color';
|
||||
|
||||
export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: RendererDrawElementOptions) {
|
||||
const { detail, angle } = elem;
|
||||
|
|
@ -14,7 +15,7 @@ export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: Re
|
|||
const centerY = y + b;
|
||||
|
||||
// draw border
|
||||
if (borderWidth && borderWidth > 0) {
|
||||
if (typeof borderWidth === 'number' && borderWidth > 0) {
|
||||
const ba = borderWidth / 2 + a;
|
||||
const bb = borderWidth / 2 + b;
|
||||
ctx.beginPath();
|
||||
|
|
@ -27,7 +28,11 @@ export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: Re
|
|||
|
||||
// draw content
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = background;
|
||||
const fillStyle = createColorStyle(ctx, background, {
|
||||
viewElementSize: { x, y, w, h },
|
||||
viewScaleInfo
|
||||
});
|
||||
ctx.fillStyle = fillStyle;
|
||||
ctx.circle(centerX, centerY, a, b, 0, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
|
|
|||
55
packages/renderer/src/draw/color.ts
Normal file
55
packages/renderer/src/draw/color.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import type { ViewContext2D, ViewScaleInfo, ElementSize, LinearGradientColor, RadialGradientColor } from '@idraw/types';
|
||||
|
||||
export function createColorStyle(
|
||||
ctx: ViewContext2D,
|
||||
color: string | LinearGradientColor | RadialGradientColor | undefined,
|
||||
opts: {
|
||||
viewElementSize: ElementSize;
|
||||
viewScaleInfo: ViewScaleInfo;
|
||||
}
|
||||
): string | CanvasPattern | CanvasGradient {
|
||||
if (typeof color === 'string') {
|
||||
return color;
|
||||
}
|
||||
const { viewElementSize, viewScaleInfo } = opts;
|
||||
const { x, y } = viewElementSize;
|
||||
const { scale } = viewScaleInfo;
|
||||
if (color?.type === 'linearGradient') {
|
||||
const { start, end, stops } = color;
|
||||
const viewStart = {
|
||||
x: x + start.x * scale,
|
||||
y: y + start.y * scale
|
||||
};
|
||||
const viewEnd = {
|
||||
x: x + end.x * scale,
|
||||
y: y + end.y * scale
|
||||
};
|
||||
|
||||
const linearGradient = ctx.createLinearGradient(viewStart.x, viewStart.y, viewEnd.x, viewEnd.y);
|
||||
stops.forEach((stop) => {
|
||||
linearGradient.addColorStop(stop.offset, stop.color);
|
||||
});
|
||||
return linearGradient;
|
||||
}
|
||||
|
||||
if (color?.type === 'radialGradient') {
|
||||
const { inner, outer, stops } = color;
|
||||
const viewInner = {
|
||||
x: x + inner.x * scale,
|
||||
y: y + inner.y * scale,
|
||||
radius: inner.radius * scale
|
||||
};
|
||||
const viewOuter = {
|
||||
x: x + outer.x * scale,
|
||||
y: y + outer.y * scale,
|
||||
radius: outer.radius * scale
|
||||
};
|
||||
const radialGradient = ctx.createRadialGradient(viewInner.x, viewInner.y, viewInner.radius, viewOuter.x, viewOuter.y, viewOuter.radius);
|
||||
stops.forEach((stop) => {
|
||||
radialGradient.addColorStop(stop.offset, stop.color);
|
||||
});
|
||||
return radialGradient;
|
||||
}
|
||||
|
||||
return '#000000';
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ export interface Data<E extends Record<string, any> = Record<string, any>> {
|
|||
assets?: ElementAssets;
|
||||
}
|
||||
|
||||
export type ColorMatrix = [
|
||||
export type Matrix = [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
|
|
@ -27,3 +27,5 @@ export type ColorMatrix = [
|
|||
number,
|
||||
number
|
||||
];
|
||||
|
||||
export type ColorMatrix = Matrix;
|
||||
|
|
|
|||
|
|
@ -42,14 +42,17 @@ export interface TransformScale {
|
|||
|
||||
export type TransformAction = TransformMatrix | TransformTranslate | TransformRotate | TransformScale;
|
||||
|
||||
export interface GradientStop {
|
||||
offset: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface LinearGradientColor {
|
||||
type: 'linearGradient';
|
||||
start: PointSize;
|
||||
end: PointSize;
|
||||
stops: Array<{
|
||||
offset: number;
|
||||
color: string;
|
||||
}>;
|
||||
stops: Array<GradientStop>;
|
||||
angle?: number;
|
||||
transform?: TransformAction[];
|
||||
}
|
||||
|
||||
|
|
@ -61,15 +64,13 @@ export interface RadialGradientColor {
|
|||
type: 'radialGradient';
|
||||
inner: GadialCircle;
|
||||
outer: GadialCircle;
|
||||
stops: Array<{
|
||||
offset: number;
|
||||
color: string;
|
||||
}>;
|
||||
stops: Array<GradientStop>;
|
||||
angle?: number;
|
||||
transform?: TransformAction[];
|
||||
}
|
||||
|
||||
export interface ElementBaseDetail {
|
||||
// boxSizing?: 'content-box' | 'border-box'; // default content-box
|
||||
boxSizing?: 'content-box' | 'border-box' | 'center-line'; // default center-line
|
||||
borderWidth?: number | [number, number, number, number]; // [top, right, bottom, left]
|
||||
borderColor?: string;
|
||||
borderRadius?: number | [number, number, number, number]; // [top-left, top-right, bottom-left, bottom-right]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export { delay, compose, throttle } from './lib/time';
|
||||
export { downloadImageFromCanvas } from './lib/file';
|
||||
export { toColorHexStr, toColorHexNum, isColorStr, colorNameToHex } from './lib/color';
|
||||
export { toColorHexStr, toColorHexNum, isColorStr, colorNameToHex, colorToCSS, colorToLinearGradientCSS } from './lib/color';
|
||||
export { createUUID, isAssetId, createAssetId } from './lib/uuid';
|
||||
export { deepClone, sortDataAsserts } from './lib/data';
|
||||
export { istype } from './lib/istype';
|
||||
|
|
@ -53,3 +53,4 @@ export { generateSVGPath, parseSVGPath } from './lib/svg-path';
|
|||
export { generateHTML, parseHTML } from './lib/html';
|
||||
export { compressImage } from './lib/image';
|
||||
export { formatNumber } from './lib/number';
|
||||
export { matrixToAngle, matrixToRadian } from './lib/matrix';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import type { LinearGradientColor, RadialGradientColor } from '@idraw/types';
|
||||
import { matrixToRadian } from '@idraw/util';
|
||||
|
||||
export function toColorHexNum(color: string): number {
|
||||
return parseInt(color.replace(/^\#/, '0x'));
|
||||
}
|
||||
|
|
@ -163,3 +166,51 @@ export function colorNameToHex(name: string): string | null {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function colorToCSS(color?: string | LinearGradientColor | RadialGradientColor): string {
|
||||
let css = 'transparent';
|
||||
if (typeof color === 'string') {
|
||||
css = color;
|
||||
} else if (color?.type === 'linearGradient') {
|
||||
const items: string[] = [];
|
||||
if (typeof color.angle === 'number') {
|
||||
items.push(`${color.angle}deg`);
|
||||
} else {
|
||||
items.push(`0deg`);
|
||||
}
|
||||
if (Array.isArray(color.stops)) {
|
||||
color.stops.forEach((stop) => {
|
||||
items.push(`${stop.color} ${stop.offset * 100}%`);
|
||||
});
|
||||
}
|
||||
css = `linear-gradient(${items.join(', ')})`;
|
||||
} else if (color?.type === 'radialGradient') {
|
||||
const items: string[] = [];
|
||||
if (Array.isArray(color.stops)) {
|
||||
color.stops.forEach((stop) => {
|
||||
items.push(`${stop.color} ${stop.offset * 100}%`);
|
||||
});
|
||||
}
|
||||
css = `radial-gradient(circle, ${items.join(', ')})`;
|
||||
}
|
||||
return css;
|
||||
}
|
||||
|
||||
export function colorToLinearGradientCSS(color?: string | LinearGradientColor | RadialGradientColor): string {
|
||||
let css = 'transparent';
|
||||
if (typeof color === 'string') {
|
||||
css = color;
|
||||
} else if (color?.type === 'radialGradient' || color?.type === 'linearGradient') {
|
||||
const items: string[] = [];
|
||||
if (Array.isArray(color.stops) && color.stops.length > 0) {
|
||||
color.stops.forEach((stop, i) => {
|
||||
items.push(`${stop.color} ${stop.offset * 100}%`);
|
||||
if (i === color.stops.length - 1 && stop.offset < 1) {
|
||||
items.push(`${stop.color} ${stop.offset * 100}%`);
|
||||
}
|
||||
});
|
||||
css = `linear-gradient(90deg, ${items.join(', ')})`;
|
||||
}
|
||||
}
|
||||
return css;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Data, Element, Elements, ElementType, ElementSize, ViewContextSize, ViewSizeInfo, RecursivePartial } from '@idraw/types';
|
||||
import { rotateElementVertexes } from './rotate';
|
||||
import { isAssetId } from './uuid';
|
||||
import { istype } from './istype';
|
||||
|
||||
// // TODO need to be deprecated
|
||||
// function getGroupIndexes(elem: Element<'group'>, uuids: string[], parentIndex: string): string[] {
|
||||
|
|
@ -296,13 +297,31 @@ function mergeElement<T extends Element<ElementType> = Element<ElementType>>(ori
|
|||
originElem[commonKey] = updateContent[commonKey];
|
||||
} else if (['detail', 'operations'].includes(commonKey)) {
|
||||
// @ts-ignore
|
||||
if (istype.json(updateContent[commonKey] as any) && istype.json(originElem[commonKey])) {
|
||||
if (istype.json(updateContent[commonKey] as any)) {
|
||||
if (!(originElem as Object)?.hasOwnProperty(commonKey)) {
|
||||
// @ts-ignore
|
||||
originElem[commonKey] = {};
|
||||
}
|
||||
// @ts-ignore
|
||||
originElem[commonKey] = { ...originElem[commonKey], ...updateContent[commonKey] };
|
||||
if (istype.json(originElem[commonKey])) {
|
||||
// @ts-ignore
|
||||
originElem[commonKey] = { ...originElem[commonKey], ...updateContent[commonKey] };
|
||||
}
|
||||
// @ts-ignore
|
||||
} else if (istype.array(updateContent[commonKey] as any) && istype.array(originElem[commonKey])) {
|
||||
} else if (istype.array(updateContent[commonKey] as any)) {
|
||||
if (!(originElem as Object)?.hasOwnProperty(commonKey)) {
|
||||
// @ts-ignore
|
||||
originElem[commonKey] = [];
|
||||
}
|
||||
// @ts-ignore
|
||||
originElem[commonKey] = { ...originElem[commonKey], ...updateContent[commonKey] };
|
||||
if (istype.array(originElem[commonKey])) {
|
||||
((updateContent as any)?.[commonKey] as Array<any>)?.forEach((item, i) => {
|
||||
// @ts-ignore
|
||||
originElem[commonKey][i] = item;
|
||||
});
|
||||
// @ts-ignore
|
||||
originElem[commonKey] = [...originElem[commonKey], ...updateContent[commonKey]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
packages/util/src/lib/matrix.ts
Normal file
28
packages/util/src/lib/matrix.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import type { Matrix } from '@idraw/types';
|
||||
|
||||
/**
|
||||
|
||||
| x_sc y_sk 0 |
|
||||
| x_sk y_sc 0 |
|
||||
| x_tr y_tr 1 |
|
||||
|
||||
https://stackoverflow.com/questions/5072271/get-angle-from-matrix
|
||||
|
||||
*/
|
||||
|
||||
export function matrixToRadian(matrix: Matrix): number | null {
|
||||
if (matrix[1] != -1 * matrix[3] || matrix[4] != matrix[0] || matrix[0] * matrix[4] - matrix[3] * matrix[1] != 1) {
|
||||
return null;
|
||||
} else {
|
||||
return Math.acos(matrix[0]);
|
||||
}
|
||||
}
|
||||
|
||||
export function matrixToAngle(matrix: Matrix): number | null {
|
||||
const radian = matrixToRadian(matrix);
|
||||
if (typeof radian === 'number') {
|
||||
const angle = (radian * 180) / Math.PI;
|
||||
return angle;
|
||||
}
|
||||
return radian;
|
||||
}
|
||||
Loading…
Reference in a new issue