From 9bf2403c6d6378ae1adf5a24ec2e2be45d2987fc Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sat, 4 Nov 2023 22:43:51 +0800 Subject: [PATCH] feat: rename gradient type and improve opacity logic --- packages/board/package.json | 3 ++- packages/core/package.json | 3 ++- packages/renderer/package.json | 3 ++- packages/renderer/src/draw/base.ts | 28 +++++++++++++++--------- packages/renderer/src/draw/circle.ts | 10 ++++++++- packages/renderer/src/draw/color.ts | 12 ++++++----- packages/types/src/lib/element.ts | 6 +++--- packages/util/src/index.ts | 5 +++-- packages/util/src/lib/color.ts | 32 +++++++++++++++++++++++----- packages/util/src/lib/element.ts | 6 ++++++ 10 files changed, 79 insertions(+), 29 deletions(-) diff --git a/packages/board/package.json b/packages/board/package.json index de92aba..183895b 100644 --- a/packages/board/package.json +++ b/packages/board/package.json @@ -23,7 +23,8 @@ "devDependencies": { "@idraw/types": "^0.4.0-alpha.0" }, - "dependencies": { + "dependencies": {}, + "peerDependencies": { "@idraw/util": "^0.4.0-alpha.3", "@idraw/renderer": "^0.4.0-alpha.3" }, diff --git a/packages/core/package.json b/packages/core/package.json index 7368a1a..de08ebb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -23,7 +23,8 @@ "devDependencies": { "@idraw/types": "^0.4.0-alpha.0" }, - "dependencies": { + "dependencies": {}, + "peerDependencies": { "@idraw/board": "^0.4.0-alpha.3", "@idraw/renderer": "^0.4.0-alpha.3", "@idraw/util": "^0.4.0-alpha.3" diff --git a/packages/renderer/package.json b/packages/renderer/package.json index 86e8c66..cb5aadc 100644 --- a/packages/renderer/package.json +++ b/packages/renderer/package.json @@ -23,7 +23,8 @@ "devDependencies": { "@idraw/types": "^0.4.0-alpha.0" }, - "dependencies": { + "dependencies": {}, + "peerDependencies": { "@idraw/util": "^0.4.0-alpha.3" }, "publishConfig": { diff --git a/packages/renderer/src/draw/base.ts b/packages/renderer/src/draw/base.ts index 97bb8b0..8d7d747 100644 --- a/packages/renderer/src/draw/base.ts +++ b/packages/renderer/src/draw/base.ts @@ -14,11 +14,6 @@ export function drawBox( viewSizeInfo: ViewSizeInfo; } ): void { - if (viewElem?.detail?.opacity !== undefined && viewElem?.detail?.opacity > 0) { - ctx.globalAlpha = viewElem.detail.opacity; - } else { - ctx.globalAlpha = 1; - } const { pattern, renderContent, originElem, calcElemSize, viewScaleInfo, viewSizeInfo } = opts || {}; drawClipPath(ctx, viewElem, { @@ -27,14 +22,19 @@ export function drawBox( viewScaleInfo, viewSizeInfo, renderContent: () => { + if (viewElem?.detail?.opacity !== undefined && viewElem?.detail?.opacity >= 0) { + ctx.globalAlpha = viewElem.detail.opacity; + } else { + ctx.globalAlpha = 1; + } drawBoxBackground(ctx, viewElem, { pattern, viewScaleInfo, viewSizeInfo }); renderContent?.(); drawBoxBorder(ctx, viewElem, { viewScaleInfo, viewSizeInfo }); // TODO // drawBoxBackground(ctx, viewElem, { pattern, viewScaleInfo, viewSizeInfo }); + ctx.globalAlpha = 1; } }); - ctx.globalAlpha = 1; } function drawClipPath( @@ -141,16 +141,18 @@ function drawBoxBackground( ctx.fillStyle = pattern as CanvasPattern; } else if (typeof viewElem.detail.background === 'string') { ctx.fillStyle = viewElem.detail.background; - } else if (viewElem.detail.background?.type === 'linearGradient') { + } else if (viewElem.detail.background?.type === 'linear-gradient') { const colorStyle = createColorStyle(ctx, viewElem.detail.background, { viewElementSize: { x, y, w, h }, - viewScaleInfo + viewScaleInfo, + opacity: ctx.globalAlpha }); ctx.fillStyle = colorStyle; - } else if (viewElem.detail.background?.type === 'radialGradient') { + } else if (viewElem.detail.background?.type === 'radial-gradient') { const colorStyle = createColorStyle(ctx, viewElem.detail.background, { viewElementSize: { x, y, w, h }, - viewScaleInfo + viewScaleInfo, + opacity: ctx.globalAlpha }); ctx.fillStyle = colorStyle; if (transform && transform.length > 0) { @@ -190,6 +192,11 @@ function drawBoxBorder(ctx: ViewContext2D, viewElem: Element, opts: if (!isColorStr(viewElem.detail.borderColor)) { return; } + if (viewElem?.detail?.opacity !== undefined && viewElem?.detail?.opacity >= 0) { + ctx.globalAlpha = viewElem.detail.opacity; + } else { + ctx.globalAlpha = 1; + } const { viewScaleInfo } = opts; const { scale } = viewScaleInfo; let borderColor = '#000000'; @@ -309,6 +316,7 @@ function drawBoxBorder(ctx: ViewContext2D, viewElem: Element, opts: ctx.arcTo(x, y, x + w, y, radiusList[0]); ctx.closePath(); ctx.stroke(); + ctx.globalAlpha = 1; } } diff --git a/packages/renderer/src/draw/circle.ts b/packages/renderer/src/draw/circle.ts index 2349acd..42432e5 100644 --- a/packages/renderer/src/draw/circle.ts +++ b/packages/renderer/src/draw/circle.ts @@ -14,6 +14,12 @@ export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: Re const centerX = x + a; const centerY = y + b; + if (elem?.detail?.opacity !== undefined && elem?.detail?.opacity >= 0) { + ctx.globalAlpha = elem.detail.opacity; + } else { + ctx.globalAlpha = 1; + } + // draw border if (typeof borderWidth === 'number' && borderWidth > 0) { const ba = borderWidth / 2 + a; @@ -30,11 +36,13 @@ export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: Re ctx.beginPath(); const fillStyle = createColorStyle(ctx, background, { viewElementSize: { x, y, w, h }, - viewScaleInfo + viewScaleInfo, + opacity: ctx.globalAlpha }); ctx.fillStyle = fillStyle; ctx.circle(centerX, centerY, a, b, 0, 0, 2 * Math.PI); ctx.closePath(); ctx.fill(); + ctx.globalAlpha = 1; }); } diff --git a/packages/renderer/src/draw/color.ts b/packages/renderer/src/draw/color.ts index 1c99e26..43fadff 100644 --- a/packages/renderer/src/draw/color.ts +++ b/packages/renderer/src/draw/color.ts @@ -1,4 +1,5 @@ import type { ViewContext2D, ViewScaleInfo, ElementSize, LinearGradientColor, RadialGradientColor } from '@idraw/types'; +import { mergeHexColorAlpha } from '@idraw/util'; export function createColorStyle( ctx: ViewContext2D, @@ -6,15 +7,16 @@ export function createColorStyle( opts: { viewElementSize: ElementSize; viewScaleInfo: ViewScaleInfo; + opacity: number; } ): string | CanvasPattern | CanvasGradient { if (typeof color === 'string') { return color; } - const { viewElementSize, viewScaleInfo } = opts; + const { viewElementSize, viewScaleInfo, opacity = 1 } = opts; const { x, y } = viewElementSize; const { scale } = viewScaleInfo; - if (color?.type === 'linearGradient') { + if (color?.type === 'linear-gradient') { const { start, end, stops } = color; const viewStart = { x: x + start.x * scale, @@ -27,12 +29,12 @@ export function createColorStyle( const linearGradient = ctx.createLinearGradient(viewStart.x, viewStart.y, viewEnd.x, viewEnd.y); stops.forEach((stop) => { - linearGradient.addColorStop(stop.offset, stop.color); + linearGradient.addColorStop(stop.offset, mergeHexColorAlpha(stop.color, opacity)); }); return linearGradient; } - if (color?.type === 'radialGradient') { + if (color?.type === 'radial-gradient') { const { inner, outer, stops } = color; const viewInner = { x: x + inner.x * scale, @@ -46,7 +48,7 @@ export function createColorStyle( }; 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); + radialGradient.addColorStop(stop.offset, mergeHexColorAlpha(stop.color, opacity)); }); return radialGradient; } diff --git a/packages/types/src/lib/element.ts b/packages/types/src/lib/element.ts index b67f9ef..d817ec5 100644 --- a/packages/types/src/lib/element.ts +++ b/packages/types/src/lib/element.ts @@ -48,7 +48,7 @@ export interface GradientStop { } export interface LinearGradientColor { - type: 'linearGradient'; + type: 'linear-gradient'; start: PointSize; end: PointSize; stops: Array; @@ -61,7 +61,7 @@ type GadialCircle = PointSize & { }; export interface RadialGradientColor { - type: 'radialGradient'; + type: 'radial-gradient'; inner: GadialCircle; outer: GadialCircle; stops: Array; @@ -79,7 +79,7 @@ export interface ElementBaseDetail { shadowOffsetX?: number; shadowOffsetY?: number; shadowBlur?: number; - color?: string; + // color?: string; background?: string | LinearGradientColor | RadialGradientColor; opacity?: number; clipPath?: ElementClipPath; diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index 052858b..a790a55 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -1,6 +1,6 @@ export { delay, compose, throttle } from './lib/time'; export { downloadImageFromCanvas } from './lib/file'; -export { toColorHexStr, toColorHexNum, isColorStr, colorNameToHex, colorToCSS, colorToLinearGradientCSS } from './lib/color'; +export { toColorHexStr, toColorHexNum, isColorStr, colorNameToHex, colorToCSS, colorToLinearGradientCSS, mergeHexColorAlpha } from './lib/color'; export { createUUID, isAssetId, createAssetId } from './lib/uuid'; export { deepClone, sortDataAsserts } from './lib/data'; export { istype } from './lib/istype'; @@ -33,7 +33,8 @@ export { findElementFromList, findElementsFromList, updateElementInList, - getGroupQueueFromList + getGroupQueueFromList, + getElementSize } from './lib/element'; export { checkRectIntersect } from './lib/rect'; export { diff --git a/packages/util/src/lib/color.ts b/packages/util/src/lib/color.ts index 1880530..a4311ee 100644 --- a/packages/util/src/lib/color.ts +++ b/packages/util/src/lib/color.ts @@ -1,5 +1,4 @@ import type { LinearGradientColor, RadialGradientColor } from '@idraw/types'; -import { matrixToRadian } from '@idraw/util'; export function toColorHexNum(color: string): number { return parseInt(color.replace(/^\#/, '0x')); @@ -171,12 +170,12 @@ export function colorToCSS(color?: string | LinearGradientColor | RadialGradient let css = 'transparent'; if (typeof color === 'string') { css = color; - } else if (color?.type === 'linearGradient') { + } else if (color?.type === 'linear-gradient') { const items: string[] = []; if (typeof color.angle === 'number') { items.push(`${color.angle}deg`); } else { - items.push(`0deg`); + items.push(`180deg`); } if (Array.isArray(color.stops)) { color.stops.forEach((stop) => { @@ -184,7 +183,7 @@ export function colorToCSS(color?: string | LinearGradientColor | RadialGradient }); } css = `linear-gradient(${items.join(', ')})`; - } else if (color?.type === 'radialGradient') { + } else if (color?.type === 'radial-gradient') { const items: string[] = []; if (Array.isArray(color.stops)) { color.stops.forEach((stop) => { @@ -200,7 +199,7 @@ export function colorToLinearGradientCSS(color?: string | LinearGradientColor | let css = 'transparent'; if (typeof color === 'string') { css = color; - } else if (color?.type === 'radialGradient' || color?.type === 'linearGradient') { + } else if (color?.type === 'radial-gradient' || color?.type === 'linear-gradient') { const items: string[] = []; if (Array.isArray(color.stops) && color.stops.length > 0) { color.stops.forEach((stop, i) => { @@ -214,3 +213,26 @@ export function colorToLinearGradientCSS(color?: string | LinearGradientColor | } return css; } + +// alpha [0, 1] +export function mergeHexColorAlpha(hex: string, alpha: number): string { + if (alpha === 1) { + return hex; + } + let hexAlpha = 1; + const regHex1 = /^\#[0-9a-f]{6,6}$/i; + const regHex2 = /^\#[0-9a-f]{8,8}$/i; + let result = hex; + if (regHex1.test(hex)) { + hexAlpha = parseInt(hex.substring(5, 7).replace(/^\#/, '0x')); + } else if (regHex2.test(hex)) { + hexAlpha = parseInt(hex.substring(7, 9).replace(/^\#/, '0x')); + result = hex.substring(0, 7); + } + hexAlpha = hexAlpha * alpha; + if (regHex1.test(result) && hexAlpha > 0 && hexAlpha < 1) { + const aHexNum = Math.max(0, Math.min(255, Math.ceil(hexAlpha * 256))); + result = `${result.toUpperCase()}${aHexNum.toString(16).toUpperCase()}`; + } + return result; +} diff --git a/packages/util/src/lib/element.ts b/packages/util/src/lib/element.ts index 6621559..7ae5c1b 100644 --- a/packages/util/src/lib/element.ts +++ b/packages/util/src/lib/element.ts @@ -344,3 +344,9 @@ export function updateElementInList( } return elements; } + +export function getElementSize(elem: Element): ElementSize { + const { x, y, w, h, angle } = elem; + const size: ElementSize = { x, y, w, h, angle }; + return size; +}