diff --git a/__tests__/demos/2d/transform-text.ts b/__tests__/demos/2d/transform-text.ts index b94dcdd86..046cc1462 100644 --- a/__tests__/demos/2d/transform-text.ts +++ b/__tests__/demos/2d/transform-text.ts @@ -1,4 +1,4 @@ -import { Circle, Text, runtime } from '../../../packages/g'; +import { Circle, Text } from '../../../packages/g'; export async function transformText(context) { const { canvas } = context; diff --git a/__tests__/demos/bugfix/1572.ts b/__tests__/demos/bugfix/1572.ts new file mode 100644 index 000000000..97023a465 --- /dev/null +++ b/__tests__/demos/bugfix/1572.ts @@ -0,0 +1,55 @@ +import { Text, Rect } from '../../../packages/g'; + +export async function gradient_text(context) { + const { canvas } = context; + await canvas.ready; + + const rect = new Rect({ + style: { + x: 0, + y: 0, + width: 100, + height: 100, + fill: 'linear-gradient(90deg, red 0%, green 100%)', + }, + }); + canvas.appendChild(rect); + + const text = new Text({ + style: { + text: `两种渐变都有一个叫做 gradientUnits(渐变单元)的属性,它描述了用来描述渐变的大小和方向的单元系统。该属性有两个值:userSpaceOnUse 、objectBoundingBox。默认值为 objectBoundingBox,我们目前看到的效果都是在这种系统下的,它大体上定义了对象的渐变大小范围,所以你只要指定从 0 到 1 的坐标值,渐变就会自动的缩放到对象相同大小。userSpaceOnUse 使用绝对单元,所以你必须知道对象的位置,并将渐变放在同样地位置上。在每个.shp,.shx与.dbf文件之中,图形在每个文件的排序是一致的。也就是说,.shp的第一条记录与.shx及.dbf之中的第一条记录相对应,如此类推。此外,在.shp与.shx之中,有许多字段的字节序是不一样的。因此用户在编写读取这些文件格式的程序时,必须十分小心地处理不同文件的不同字节序。`, + x: 0, + y: 0, + // dy: 10, + textBaseline: 'top', + wordWrap: true, + wordWrapWidth: 800, + fill: 'linear-gradient(90deg, red 0%, green 100%)', + }, + }); + canvas.appendChild(text); + + const rect2 = new Rect({ + style: { + x: 0, + y: 200, + width: 100, + height: 100, + fill: 'linear-gradient(90deg, red 0.1%, green 100%)', + }, + }); + canvas.appendChild(rect2); + const text2 = new Text({ + style: { + text: `两种渐变都有一个叫做 gradientUnits(渐变单元)的属性,它描述了用来描述渐变的大小和方向的单元系统。该属性有两个值:userSpaceOnUse 、objectBoundingBox。默认值为 objectBoundingBox,我们目前看到的效果都是在这种系统下的,它大体上定义了对象的渐变大小范围,所以你只要指定从 0 到 1 的坐标值,渐变就会自动的缩放到对象相同大小。userSpaceOnUse 使用绝对单元,所以你必须知道对象的位置,并将渐变放在同样地位置上。在每个.shp,.shx与.dbf文件之中,图形在每个文件的排序是一致的。也就是说,.shp的第一条记录与.shx及.dbf之中的第一条记录相对应,如此类推。此外,在.shp与.shx之中,有许多字段的字节序是不一样的。因此用户在编写读取这些文件格式的程序时,必须十分小心地处理不同文件的不同字节序。`, + x: 0, + y: 200, + // dy: 10, + textBaseline: 'top', + wordWrap: true, + wordWrapWidth: 800, + fill: 'linear-gradient(90deg, red 0.1%, green 100%)', + }, + }); + canvas.appendChild(text2); +} diff --git a/__tests__/demos/bugfix/index.ts b/__tests__/demos/bugfix/index.ts index 6e6cc43a6..9c67d18f0 100644 --- a/__tests__/demos/bugfix/index.ts +++ b/__tests__/demos/bugfix/index.ts @@ -4,3 +4,4 @@ export { scale0 } from './scale0'; export { dirty } from './dirty'; export { image } from './1636'; export { shadowroot_offset } from './1677'; +export { gradient_text } from './1572'; diff --git a/jest.config.js b/jest.config.js index 617cbe372..d38c29044 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,6 +6,12 @@ const packages = readdirSync(basePath).filter((name) => { return lstatSync(path.join(basePath, name)).isDirectory(); }); +// Installing third-party modules by tnpm or cnpm will name modules with underscore as prefix. +// In this case _{module} is also necessary. +const esm = ['internmap', 'd3-*', 'lodash-es'] + .map((d) => `_${d}|${d}`) + .join('|'); + // @see https://blog.ah.technology/a-guide-through-the-wild-wild-west-of-setting-up-a-mono-repo-part-2-adding-jest-with-a-breeze-16e08596f0de const moduleNameMapper = { ...packages.reduce( @@ -60,8 +66,5 @@ module.exports = { }, moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], modulePathIgnorePatterns: ['dist'], - transformIgnorePatterns: [ - // @see https://stackoverflow.com/a/69179139 - '/node_modules/(?!d3|d3-array|internmap|delaunator|robust-predicates)', - ], + transformIgnorePatterns: [`/node_modules/(?!(?:.pnpm/)?(${esm}))`], }; diff --git a/jest.node.config.js b/jest.node.config.js index 51321756b..69ef4cc19 100644 --- a/jest.node.config.js +++ b/jest.node.config.js @@ -6,6 +6,12 @@ const packages = readdirSync(basePath).filter((name) => { return lstatSync(path.join(basePath, name)).isDirectory(); }); +// Installing third-party modules by tnpm or cnpm will name modules with underscore as prefix. +// In this case _{module} is also necessary. +const esm = ['internmap', 'd3-*', 'lodash-es'] + .map((d) => `_${d}|${d}`) + .join('|'); + // @see https://blog.ah.technology/a-guide-through-the-wild-wild-west-of-setting-up-a-mono-repo-part-2-adding-jest-with-a-breeze-16e08596f0de const moduleNameMapper = { ...packages.reduce( @@ -40,12 +46,8 @@ module.exports = { }, moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], modulePathIgnorePatterns: ['dist'], - transformIgnorePatterns: [ - // @see https://stackoverflow.com/a/69179139 - '/node_modules/(?!d3|d3-array|internmap|delaunator|robust-predicates)', - ], globalSetup: './scripts/jest/setup.js', globalTeardown: './scripts/jest/teardown.js', testEnvironment: './scripts/jest/environment.js', - testPathIgnorePatterns: ['/__tests__/main.ts'], + transformIgnorePatterns: [`/node_modules/(?!(?:.pnpm/)?(${esm}))`], }; diff --git a/packages/g-canvas/CHANGELOG.md b/packages/g-canvas/CHANGELOG.md index 13dbfddc4..ddacfe537 100644 --- a/packages/g-canvas/CHANGELOG.md +++ b/packages/g-canvas/CHANGELOG.md @@ -1,5 +1,14 @@ # @antv/g-canvas +## 2.0.6 + +### Patch Changes + +- Updated dependencies [a4d7c7e0] +- Updated dependencies [a4d7c7e0] + - @antv/g-plugin-canvas-renderer@2.0.6 + - @antv/g-plugin-canvas-picker@2.0.6 + ## 2.0.5 ### Patch Changes diff --git a/packages/g-canvas/package.json b/packages/g-canvas/package.json index 852b5605a..bec3de5ff 100644 --- a/packages/g-canvas/package.json +++ b/packages/g-canvas/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-canvas", - "version": "2.0.5", + "version": "2.0.6", "description": "A renderer implemented by Canvas 2D API", "keywords": [ "antv", diff --git a/packages/g-canvaskit/CHANGELOG.md b/packages/g-canvaskit/CHANGELOG.md index 7edc0f407..43cb20d5f 100644 --- a/packages/g-canvaskit/CHANGELOG.md +++ b/packages/g-canvaskit/CHANGELOG.md @@ -1,5 +1,11 @@ # @antv/g-canvaskit +## 1.0.6 + +### Patch Changes + +- @antv/g-plugin-canvas-picker@2.0.6 + ## 1.0.5 ### Patch Changes diff --git a/packages/g-canvaskit/package.json b/packages/g-canvaskit/package.json index 032af1af6..498ad0e4d 100644 --- a/packages/g-canvaskit/package.json +++ b/packages/g-canvaskit/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-canvaskit", - "version": "1.0.5", + "version": "1.0.6", "description": "A renderer implemented by CanvasKit", "keywords": [ "antv", diff --git a/packages/g-mobile-canvas/CHANGELOG.md b/packages/g-mobile-canvas/CHANGELOG.md index dd9543714..08b490739 100644 --- a/packages/g-mobile-canvas/CHANGELOG.md +++ b/packages/g-mobile-canvas/CHANGELOG.md @@ -1,5 +1,14 @@ # @antv/g-mobile-canvas +## 1.0.6 + +### Patch Changes + +- Updated dependencies [a4d7c7e0] +- Updated dependencies [a4d7c7e0] + - @antv/g-plugin-canvas-renderer@2.0.6 + - @antv/g-plugin-canvas-picker@2.0.6 + ## 1.0.5 ### Patch Changes diff --git a/packages/g-mobile-canvas/package.json b/packages/g-mobile-canvas/package.json index 9eb882fe1..aa47d0a4e 100644 --- a/packages/g-mobile-canvas/package.json +++ b/packages/g-mobile-canvas/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-mobile-canvas", - "version": "1.0.5", + "version": "1.0.6", "description": "A renderer implemented with Canvas2D API in mobile environment", "keywords": [ "antv", diff --git a/packages/g-mobile-svg/CHANGELOG.md b/packages/g-mobile-svg/CHANGELOG.md index d6274369d..025eaade9 100644 --- a/packages/g-mobile-svg/CHANGELOG.md +++ b/packages/g-mobile-svg/CHANGELOG.md @@ -1,5 +1,13 @@ # @antv/g-mobile-svg +## 1.0.7 + +### Patch Changes + +- Updated dependencies [a4d7c7e0] + - @antv/g-plugin-svg-renderer@2.0.7 + - @antv/g-plugin-svg-picker@2.0.7 + ## 1.0.6 ### Patch Changes diff --git a/packages/g-mobile-svg/package.json b/packages/g-mobile-svg/package.json index 9536cca46..4939e3330 100644 --- a/packages/g-mobile-svg/package.json +++ b/packages/g-mobile-svg/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-mobile-svg", - "version": "1.0.6", + "version": "1.0.7", "description": "A renderer implemented by SVG in mobile environment", "keywords": [ "antv", diff --git a/packages/g-plugin-canvas-picker/CHANGELOG.md b/packages/g-plugin-canvas-picker/CHANGELOG.md index 1f38e4677..13be097e7 100644 --- a/packages/g-plugin-canvas-picker/CHANGELOG.md +++ b/packages/g-plugin-canvas-picker/CHANGELOG.md @@ -1,5 +1,13 @@ # @antv/g-plugin-canvas-picker +## 2.0.6 + +### Patch Changes + +- Updated dependencies [a4d7c7e0] +- Updated dependencies [a4d7c7e0] + - @antv/g-plugin-canvas-renderer@2.0.6 + ## 2.0.5 ### Patch Changes diff --git a/packages/g-plugin-canvas-picker/package.json b/packages/g-plugin-canvas-picker/package.json index 0b5f86101..da8a1ae0a 100644 --- a/packages/g-plugin-canvas-picker/package.json +++ b/packages/g-plugin-canvas-picker/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-canvas-picker", - "version": "2.0.5", + "version": "2.0.6", "description": "A G plugin for picking in canvas", "keywords": [ "antv", diff --git a/packages/g-plugin-canvas-renderer/CHANGELOG.md b/packages/g-plugin-canvas-renderer/CHANGELOG.md index 749c52f87..081f43022 100644 --- a/packages/g-plugin-canvas-renderer/CHANGELOG.md +++ b/packages/g-plugin-canvas-renderer/CHANGELOG.md @@ -1,5 +1,12 @@ # @antv/g-plugin-canvas-renderer +## 2.0.6 + +### Patch Changes + +- a4d7c7e0: Support gradient text in canvas & svg renderer. +- a4d7c7e0: Render correctly in canvas when stroke is emtpy. + ## 2.0.5 ### Patch Changes diff --git a/packages/g-plugin-canvas-renderer/package.json b/packages/g-plugin-canvas-renderer/package.json index 0fabe0f8f..80f9c5469 100644 --- a/packages/g-plugin-canvas-renderer/package.json +++ b/packages/g-plugin-canvas-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-canvas-renderer", - "version": "2.0.5", + "version": "2.0.6", "description": "A G plugin of renderer implementation with Canvas2D API", "keywords": [ "antv", diff --git a/packages/g-plugin-canvas-renderer/src/index.ts b/packages/g-plugin-canvas-renderer/src/index.ts index 26f355151..2665ed1e0 100644 --- a/packages/g-plugin-canvas-renderer/src/index.ts +++ b/packages/g-plugin-canvas-renderer/src/index.ts @@ -35,7 +35,7 @@ export class Plugin extends AbstractRendererPlugin<{ [Shape.ELLIPSE]: defaultRenderer, [Shape.RECT]: defaultRenderer, [Shape.IMAGE]: new ImageRenderer(imagePool), - [Shape.TEXT]: new TextRenderer(), + [Shape.TEXT]: new TextRenderer(imagePool), [Shape.LINE]: defaultRenderer, [Shape.POLYLINE]: defaultRenderer, [Shape.POLYGON]: defaultRenderer, @@ -48,7 +48,9 @@ export class Plugin extends AbstractRendererPlugin<{ this.context.defaultStyleRendererFactory = defaultStyleRendererFactory; this.context.styleRendererFactory = defaultStyleRendererFactory; - this.addRenderingPlugin(new CanvasRendererPlugin(canvasRendererPluginOptions)); + this.addRenderingPlugin( + new CanvasRendererPlugin(canvasRendererPluginOptions), + ); } destroy(): void { this.removeAllRenderingPlugins(); diff --git a/packages/g-plugin-canvas-renderer/src/shapes/styles/Default.ts b/packages/g-plugin-canvas-renderer/src/shapes/styles/Default.ts index 7d5898c3a..961e31419 100644 --- a/packages/g-plugin-canvas-renderer/src/shapes/styles/Default.ts +++ b/packages/g-plugin-canvas-renderer/src/shapes/styles/Default.ts @@ -43,9 +43,9 @@ export class DefaultRenderer implements StyleRenderer { filter, miterLimit, } = parsedStyle; - const hasFill = !isNil(fill) && !(fill as CSSRGB).isNone; - const hasStroke = - !isNil(stroke) && !(stroke as CSSRGB).isNone && lineWidth > 0; + const hasFill = fill && !(fill as CSSRGB).isNone; + const hasStroke = stroke && !(stroke as CSSRGB).isNone && lineWidth > 0; + const isFillTransparent = (fill as CSSRGB)?.alpha === 0; const hasFilter = !!(filter && filter.length); const hasShadow = !isNil(shadowColor) && shadowBlur > 0; @@ -67,7 +67,7 @@ export class DefaultRenderer implements StyleRenderer { setShadowAndFilter(object, context, hasShadow); } - this.fill( + applyFill( context, object, fill, @@ -75,6 +75,7 @@ export class DefaultRenderer implements StyleRenderer { canvasContext, plugin, runtime, + this.imagePool, ); if (!shouldDrawShadowWithStroke) { @@ -104,13 +105,29 @@ export class DefaultRenderer implements StyleRenderer { setShadowAndFilter(object, context, true); if (isInnerShadow) { - this.stroke(context, object, stroke, canvasContext, plugin, runtime); + applyStroke( + context, + object, + stroke, + canvasContext, + plugin, + runtime, + this.imagePool, + ); context.globalCompositeOperation = 'source-over'; this.clearShadowAndFilter(context, hasFilter, true); } } - this.stroke(context, object, stroke, canvasContext, plugin, runtime); + applyStroke( + context, + object, + stroke, + canvasContext, + plugin, + runtime, + this.imagePool, + ); } } @@ -133,177 +150,192 @@ export class DefaultRenderer implements StyleRenderer { } } } +} - private fill( - context: CanvasRenderingContext2D, - object: DisplayObject, - fill: CSSRGB | CSSGradientValue[] | Pattern, - fillRule: 'nonzero' | 'evenodd', - canvasContext: CanvasContext, - plugin: CanvasRendererPlugin, - runtime: GlobalRuntime, - ) { - if (Array.isArray(fill)) { - fill.forEach((gradient) => { - context.fillStyle = this.getColor(gradient, object, context); +/** + * apply before fill and stroke but only once + */ +export function setShadowAndFilter( + object: DisplayObject, + context: CanvasRenderingContext2D, + hasShadow: boolean, +) { + const { filter, shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY } = + object.parsedStyle as ParsedBaseStyleProps; - fillRule ? context.fill(fillRule) : context.fill(); - }); - } else { - if (isPattern(fill)) { - context.fillStyle = this.getPattern( - fill, - object, - context, - canvasContext, - plugin, - runtime, - ); - } - fillRule ? context.fill(fillRule) : context.fill(); - } + if (filter && filter.length) { + // use raw filter string + context.filter = object.style.filter; } - private stroke( - context: CanvasRenderingContext2D, - object: DisplayObject, - stroke: CSSRGB | CSSGradientValue[] | Pattern, - canvasContext: CanvasContext, - plugin: CanvasRendererPlugin, - runtime: GlobalRuntime, - ) { - if (Array.isArray(stroke)) { - stroke.forEach((gradient) => { - context.strokeStyle = this.getColor(gradient, object, context); - context.stroke(); - }); - } else { - if (isPattern(stroke)) { - context.strokeStyle = this.getPattern( - stroke, - object, - context, - canvasContext, - plugin, - runtime, - ); - } - context.stroke(); - } + if (hasShadow) { + context.shadowColor = shadowColor.toString(); + context.shadowBlur = shadowBlur || 0; + context.shadowOffsetX = shadowOffsetX || 0; + context.shadowOffsetY = shadowOffsetY || 0; } +} - private getPattern( - pattern: Pattern, - object: DisplayObject, - context: CanvasRenderingContext2D, - canvasContext: CanvasContext, - plugin: CanvasRendererPlugin, - runtime: GlobalRuntime, - ): CanvasPattern { - let $offscreenCanvas: HTMLCanvasElement; - let dpr: number; - if ((pattern.image as Rect).nodeName === 'rect') { - const { width, height } = (pattern.image as Rect).parsedStyle; - dpr = canvasContext.contextService.getDPR(); - const { offscreenCanvas } = canvasContext.config; - $offscreenCanvas = runtime.offscreenCanvasCreator.getOrCreateCanvas( +export function getPattern( + pattern: Pattern, + object: DisplayObject, + context: CanvasRenderingContext2D, + canvasContext: CanvasContext, + plugin: CanvasRendererPlugin, + runtime: GlobalRuntime, + imagePool: ImagePool, +): CanvasPattern { + let $offscreenCanvas: HTMLCanvasElement; + let dpr: number; + if ((pattern.image as Rect).nodeName === 'rect') { + const { width, height } = (pattern.image as Rect).parsedStyle; + dpr = canvasContext.contextService.getDPR(); + const { offscreenCanvas } = canvasContext.config; + $offscreenCanvas = runtime.offscreenCanvasCreator.getOrCreateCanvas( + offscreenCanvas, + ) as HTMLCanvasElement; + + $offscreenCanvas.width = width * dpr; + $offscreenCanvas.height = height * dpr; + + const offscreenCanvasContext = + runtime.offscreenCanvasCreator.getOrCreateContext( offscreenCanvas, - ) as HTMLCanvasElement; + ) as CanvasRenderingContext2D; - $offscreenCanvas.width = width * dpr; - $offscreenCanvas.height = height * dpr; + const restoreStack = []; - const offscreenCanvasContext = - runtime.offscreenCanvasCreator.getOrCreateContext( - offscreenCanvas, - ) as CanvasRenderingContext2D; + // offscreenCanvasContext.scale(1 / dpr, 1 / dpr); - const restoreStack = []; + (pattern.image as Rect).forEach((object: DisplayObject) => { + plugin.renderDisplayObject( + object, + offscreenCanvasContext, + canvasContext, + restoreStack, + runtime, + ); + }); - // offscreenCanvasContext.scale(1 / dpr, 1 / dpr); + restoreStack.forEach(() => { + offscreenCanvasContext.restore(); + }); + } - (pattern.image as Rect).forEach((object: DisplayObject) => { - plugin.renderDisplayObject( - object, - offscreenCanvasContext, - canvasContext, - restoreStack, - runtime, - ); - }); + const canvasPattern = imagePool.getOrCreatePatternSync( + pattern, + context, + $offscreenCanvas, + dpr, + object.getGeometryBounds().min, + () => { + // set dirty rectangle flag + object.renderable.dirty = true; + canvasContext.renderingService.dirtify(); + }, + ); + + return canvasPattern; +} - restoreStack.forEach(() => { - offscreenCanvasContext.restore(); - }); - } +export function getColor( + parsedColor: CSSGradientValue, + object: DisplayObject, + context: CanvasRenderingContext2D, + imagePool: ImagePool, +) { + let color: CanvasGradient | string; - const canvasPattern = this.imagePool.getOrCreatePatternSync( - pattern, - context, - $offscreenCanvas, - dpr, - object.getGeometryBounds().min, - () => { - // set dirty rectangle flag - object.renderable.dirty = true; - canvasContext.renderingService.dirtify(); + if ( + parsedColor.type === GradientType.LinearGradient || + parsedColor.type === GradientType.RadialGradient + ) { + const bounds = object.getGeometryBounds(); + const width = (bounds && bounds.halfExtents[0] * 2) || 1; + const height = (bounds && bounds.halfExtents[1] * 2) || 1; + const min = (bounds && bounds.min) || [0, 0]; + color = imagePool.getOrCreateGradient( + { + type: parsedColor.type, + ...(parsedColor.value as LinearGradient & RadialGradient), + min: min as [number, number], + width, + height, }, + context, ); - - return canvasPattern; } - private getColor( - parsedColor: CSSGradientValue, - object: DisplayObject, - context: CanvasRenderingContext2D, - ) { - let color: CanvasGradient | string; - - if ( - parsedColor.type === GradientType.LinearGradient || - parsedColor.type === GradientType.RadialGradient - ) { - const bounds = object.getGeometryBounds(); - const width = (bounds && bounds.halfExtents[0] * 2) || 1; - const height = (bounds && bounds.halfExtents[1] * 2) || 1; - const min = (bounds && bounds.min) || [0, 0]; - color = this.imagePool.getOrCreateGradient( - { - type: parsedColor.type, - ...(parsedColor.value as LinearGradient & RadialGradient), - min: min as [number, number], - width, - height, - }, + return color; +} + +export function applyFill( + context: CanvasRenderingContext2D, + object: DisplayObject, + fill: CSSRGB | CSSGradientValue[] | Pattern, + fillRule: 'nonzero' | 'evenodd', + canvasContext: CanvasContext, + plugin: CanvasRendererPlugin, + runtime: GlobalRuntime, + imagePool: ImagePool, + skipFill = false, +) { + if (Array.isArray(fill)) { + fill.forEach((gradient) => { + context.fillStyle = getColor(gradient, object, context, imagePool); + if (!skipFill) { + fillRule ? context.fill(fillRule) : context.fill(); + } + }); + } else { + if (isPattern(fill)) { + context.fillStyle = getPattern( + fill, + object, context, + canvasContext, + plugin, + runtime, + imagePool, ); } - - return color; + if (!skipFill) { + fillRule ? context.fill(fillRule) : context.fill(); + } } } -/** - * apply before fill and stroke but only once - */ -export function setShadowAndFilter( - object: DisplayObject, +export function applyStroke( context: CanvasRenderingContext2D, - hasShadow: boolean, + object: DisplayObject, + stroke: CSSRGB | CSSGradientValue[] | Pattern, + canvasContext: CanvasContext, + plugin: CanvasRendererPlugin, + runtime: GlobalRuntime, + imagePool: ImagePool, + skipStroke = false, ) { - const { filter, shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY } = - object.parsedStyle as ParsedBaseStyleProps; - - if (filter && filter.length) { - // use raw filter string - context.filter = object.style.filter; - } - - if (hasShadow) { - context.shadowColor = shadowColor.toString(); - context.shadowBlur = shadowBlur || 0; - context.shadowOffsetX = shadowOffsetX || 0; - context.shadowOffsetY = shadowOffsetY || 0; + if (Array.isArray(stroke)) { + stroke.forEach((gradient) => { + context.strokeStyle = getColor(gradient, object, context, imagePool); + if (!skipStroke) { + context.stroke(); + } + }); + } else { + if (isPattern(stroke)) { + context.strokeStyle = getPattern( + stroke, + object, + context, + canvasContext, + plugin, + runtime, + imagePool, + ); + } + if (!skipStroke) { + context.stroke(); + } } } diff --git a/packages/g-plugin-canvas-renderer/src/shapes/styles/Text.ts b/packages/g-plugin-canvas-renderer/src/shapes/styles/Text.ts index 6466b1c7c..dddd22fe7 100644 --- a/packages/g-plugin-canvas-renderer/src/shapes/styles/Text.ts +++ b/packages/g-plugin-canvas-renderer/src/shapes/styles/Text.ts @@ -1,17 +1,22 @@ -import type { - CSSRGB, - CanvasContext, - DisplayObject, - GlobalRuntime, - ParsedTextStyleProps, - Rectangle, +import { + type CSSRGB, + type CanvasContext, + type DisplayObject, + type GlobalRuntime, + type ParsedTextStyleProps, + type Rectangle, + CSSGradientValue, + Pattern, } from '@antv/g-lite'; import { isNil } from '@antv/util'; -import { setShadowAndFilter } from './Default'; +import { applyFill, applyStroke, setShadowAndFilter } from './Default'; import type { StyleRenderer } from './interfaces'; import { CanvasRendererPlugin } from '../../CanvasRendererPlugin'; +import { ImagePool } from '@antv/g-plugin-image-loader'; export class TextRenderer implements StyleRenderer { + constructor(private imagePool: ImagePool) {} + render( context: CanvasRenderingContext2D, parsedStyle: ParsedTextStyleProps, @@ -31,6 +36,7 @@ export class TextRenderer implements StyleRenderer { letterSpacing = 0, stroke, fill, + fillRule, fillOpacity = 1, strokeOpacity = 1, opacity = 1, @@ -105,30 +111,45 @@ export class TextRenderer implements StyleRenderer { if (!isNil(stroke) && !(stroke as CSSRGB).isNone && lineWidth) { this.drawLetterSpacing( context, + object, lines[i], lineMetrics[i], textAlign, linePositionX, linePositionY, letterSpacing, + fill, + fillRule, fillOpacity, + stroke, strokeOpacity, opacity, true, + canvasContext, + plugin, + runtime, ); } if (!isNil(fill)) { this.drawLetterSpacing( context, + object, lines[i], lineMetrics[i], textAlign, linePositionX, linePositionY, letterSpacing, + fill, + fillRule, fillOpacity, + stroke, strokeOpacity, opacity, + false, + canvasContext, + plugin, + runtime, ); } } @@ -136,23 +157,54 @@ export class TextRenderer implements StyleRenderer { private drawLetterSpacing( context: CanvasRenderingContext2D, + object: DisplayObject, text: string, lineMetrics: Rectangle, textAlign: CanvasTextAlign | 'middle', x: number, y: number, letterSpacing: number, + fill: CSSRGB | CSSGradientValue[] | Pattern, + fillRule: 'nonzero' | 'evenodd', fillOpacity: number | undefined, + stroke: CSSRGB | CSSGradientValue[] | Pattern, strokeOpacity: number | undefined, opacity: number | undefined, - isStroke = false, + isStroke: boolean, + canvasContext: CanvasContext, + plugin: CanvasRendererPlugin, + runtime: GlobalRuntime, ): void { // letterSpacing of 0 means normal, render all texts directly if (letterSpacing === 0) { if (isStroke) { - this.strokeText(context, text, x, y, strokeOpacity); + this.strokeText( + context, + object, + text, + x, + y, + stroke, + strokeOpacity, + canvasContext, + plugin, + runtime, + ); } else { - this.fillText(context, text, x, y, fillOpacity, opacity); + this.fillText( + context, + object, + text, + x, + y, + fill, + fillRule, + fillOpacity, + opacity, + canvasContext, + plugin, + runtime, + ); } return; } @@ -176,19 +228,30 @@ export class TextRenderer implements StyleRenderer { if (isStroke) { this.strokeText( context, + object, currentChar, currentPosition, y, + stroke, strokeOpacity, + canvasContext, + plugin, + runtime, ); } else { this.fillText( context, + object, currentChar, currentPosition, y, + fill, + fillRule, fillOpacity, opacity, + canvasContext, + plugin, + runtime, ); } currentWidth = context.measureText(text.substring(i + 1)).width; @@ -201,12 +264,30 @@ export class TextRenderer implements StyleRenderer { private fillText( context: CanvasRenderingContext2D, + object: DisplayObject, text: string, x: number, y: number, + fill: CSSRGB | CSSGradientValue[] | Pattern, + fillRule: 'nonzero' | 'evenodd', fillOpacity: number | undefined, opacity: number | undefined, + canvasContext: CanvasContext, + plugin: CanvasRendererPlugin, + runtime: GlobalRuntime, ) { + applyFill( + context, + object, + fill, + fillRule, + canvasContext, + plugin, + runtime, + this.imagePool, + true, + ); + let currentGlobalAlpha: number; const applyOpacity = !isNil(fillOpacity) && fillOpacity !== 1; if (applyOpacity) { @@ -221,11 +302,27 @@ export class TextRenderer implements StyleRenderer { private strokeText( context: CanvasRenderingContext2D, + object: DisplayObject, text: string, x: number, y: number, + stroke: CSSRGB | CSSGradientValue[] | Pattern, strokeOpacity: number | undefined, + canvasContext: CanvasContext, + plugin: CanvasRendererPlugin, + runtime: GlobalRuntime, ) { + applyStroke( + context, + object, + stroke, + canvasContext, + plugin, + runtime, + this.imagePool, + true, + ); + let currentGlobalAlpha: number; const applyOpacity = !isNil(strokeOpacity) && strokeOpacity !== 1; if (applyOpacity) { diff --git a/packages/g-plugin-canvaskit-renderer/src/CanvaskitRendererPlugin.ts b/packages/g-plugin-canvaskit-renderer/src/CanvaskitRendererPlugin.ts index f37618251..695665bc0 100644 --- a/packages/g-plugin-canvaskit-renderer/src/CanvaskitRendererPlugin.ts +++ b/packages/g-plugin-canvaskit-renderer/src/CanvaskitRendererPlugin.ts @@ -565,9 +565,9 @@ export class CanvaskitRendererPlugin implements RenderingPlugin { canvas.rotate(rot, 0, 0); canvas.scale(sx, sy); - const hasFill = !isNil(fill) && !(fill as CSSRGB).isNone; - const hasStroke = !isNil(stroke) && !(stroke as CSSRGB).isNone; - const hasShadow = !isNil(shadowColor) && shadowBlur > 0; + const hasFill = fill && !(fill as CSSRGB).isNone; + const hasStroke = stroke && !(stroke as CSSRGB).isNone; + const hasShadow = shadowColor && shadowBlur > 0; let fillPaint: Paint = null; let strokePaint: Paint = null; diff --git a/packages/g-plugin-rough-canvas-renderer/CHANGELOG.md b/packages/g-plugin-rough-canvas-renderer/CHANGELOG.md index fe15460e9..e8b119c36 100644 --- a/packages/g-plugin-rough-canvas-renderer/CHANGELOG.md +++ b/packages/g-plugin-rough-canvas-renderer/CHANGELOG.md @@ -1,5 +1,11 @@ # @antv/g-plugin-rough-canvas-renderer +## 2.0.7 + +### Patch Changes + +- @antv/g-canvas@2.0.6 + ## 2.0.6 ### Patch Changes diff --git a/packages/g-plugin-rough-canvas-renderer/package.json b/packages/g-plugin-rough-canvas-renderer/package.json index a717dc3e5..1005b0394 100644 --- a/packages/g-plugin-rough-canvas-renderer/package.json +++ b/packages/g-plugin-rough-canvas-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-rough-canvas-renderer", - "version": "2.0.6", + "version": "2.0.7", "description": "A G plugin of renderer implementation with rough.js", "keywords": [ "antv", diff --git a/packages/g-plugin-rough-svg-renderer/CHANGELOG.md b/packages/g-plugin-rough-svg-renderer/CHANGELOG.md index 32cde6c63..2946d2f32 100644 --- a/packages/g-plugin-rough-svg-renderer/CHANGELOG.md +++ b/packages/g-plugin-rough-svg-renderer/CHANGELOG.md @@ -1,5 +1,11 @@ # @antv/g-plugin-rough-svg-renderer +## 2.0.8 + +### Patch Changes + +- @antv/g-svg@2.0.7 + ## 2.0.7 ### Patch Changes diff --git a/packages/g-plugin-rough-svg-renderer/package.json b/packages/g-plugin-rough-svg-renderer/package.json index afd5396bd..3c01b89e1 100644 --- a/packages/g-plugin-rough-svg-renderer/package.json +++ b/packages/g-plugin-rough-svg-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-rough-svg-renderer", - "version": "2.0.7", + "version": "2.0.8", "description": "A G plugin of renderer implementation with rough.js", "keywords": [ "antv", diff --git a/packages/g-plugin-svg-picker/CHANGELOG.md b/packages/g-plugin-svg-picker/CHANGELOG.md index cb1dcd1c8..881518a1d 100644 --- a/packages/g-plugin-svg-picker/CHANGELOG.md +++ b/packages/g-plugin-svg-picker/CHANGELOG.md @@ -1,5 +1,12 @@ # @antv/g-plugin-svg-picker +## 2.0.7 + +### Patch Changes + +- Updated dependencies [a4d7c7e0] + - @antv/g-plugin-svg-renderer@2.0.7 + ## 2.0.6 ### Patch Changes diff --git a/packages/g-plugin-svg-picker/package.json b/packages/g-plugin-svg-picker/package.json index 6c5f90742..e8b4497cb 100644 --- a/packages/g-plugin-svg-picker/package.json +++ b/packages/g-plugin-svg-picker/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-svg-picker", - "version": "2.0.6", + "version": "2.0.7", "description": "A G plugin for picking in SVG", "keywords": [ "antv", diff --git a/packages/g-plugin-svg-renderer/CHANGELOG.md b/packages/g-plugin-svg-renderer/CHANGELOG.md index 5148d1a40..c99d124c5 100644 --- a/packages/g-plugin-svg-renderer/CHANGELOG.md +++ b/packages/g-plugin-svg-renderer/CHANGELOG.md @@ -1,5 +1,11 @@ # @antv/g-plugin-svg-renderer +## 2.0.7 + +### Patch Changes + +- a4d7c7e0: Support gradient text in canvas & svg renderer. + ## 2.0.6 ### Patch Changes diff --git a/packages/g-plugin-svg-renderer/package.json b/packages/g-plugin-svg-renderer/package.json index 506ede5c3..015558c96 100644 --- a/packages/g-plugin-svg-renderer/package.json +++ b/packages/g-plugin-svg-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-svg-renderer", - "version": "2.0.6", + "version": "2.0.7", "description": "A G plugin of renderer implementation with SVG", "keywords": [ "antv", diff --git a/packages/g-plugin-svg-renderer/src/shapes/defs/Pattern.ts b/packages/g-plugin-svg-renderer/src/shapes/defs/Pattern.ts index a19101cce..03ef66f61 100644 --- a/packages/g-plugin-svg-renderer/src/shapes/defs/Pattern.ts +++ b/packages/g-plugin-svg-renderer/src/shapes/defs/Pattern.ts @@ -107,15 +107,15 @@ function generateCacheKey( type === GradientType.RadialGradient ) { // @ts-ignore - const { type, width, height, steps, angle, cx, cy, size } = { + const { type, x, y, width, height, steps, angle, cx, cy, size } = { ...value, ...options, }; - cacheKey = `gradient-${type}-${angle?.toString() || 0}-${ - cx?.toString() || 0 - }-${cy?.toString() || 0}-${ - size?.toString() || 0 - }-${width}-${height}-${steps + cacheKey = `gradient-${type}-${x?.toString() || 0}-${ + y?.toString() || 0 + }-${angle?.toString() || 0}-${cx?.toString() || 0}-${ + cy?.toString() || 0 + }-${size?.toString() || 0}-${width}-${height}-${steps .map(({ offset, color }) => `${offset}${color}`) .join('-')}`; } @@ -341,7 +341,12 @@ function createOrUpdateGradient( const height = (bounds && bounds.halfExtents[1] * 2) || 0; const min = (bounds && bounds.min) || [0, 0]; - const gradientId = generateCacheKey(parsedColor, { width, height }); + const gradientId = generateCacheKey(parsedColor, { + x: min[0], + y: min[1], + width, + height, + }); let $existed = $def.querySelector(`#${gradientId}`); if (!$existed) { diff --git a/packages/g-plugin-zdog-canvas-renderer/CHANGELOG.md b/packages/g-plugin-zdog-canvas-renderer/CHANGELOG.md index d620da2f0..f9708d17c 100644 --- a/packages/g-plugin-zdog-canvas-renderer/CHANGELOG.md +++ b/packages/g-plugin-zdog-canvas-renderer/CHANGELOG.md @@ -1,5 +1,11 @@ # @antv/g-plugin-zdog-canvas-renderer +## 2.0.6 + +### Patch Changes + +- @antv/g-canvas@2.0.6 + ## 2.0.5 ### Patch Changes diff --git a/packages/g-plugin-zdog-canvas-renderer/package.json b/packages/g-plugin-zdog-canvas-renderer/package.json index 5dfa80dcc..3cbc4586d 100644 --- a/packages/g-plugin-zdog-canvas-renderer/package.json +++ b/packages/g-plugin-zdog-canvas-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-zdog-canvas-renderer", - "version": "2.0.5", + "version": "2.0.6", "description": "A G plugin of renderer implementation with Zdog", "keywords": [ "antv", diff --git a/packages/g-plugin-zdog-svg-renderer/CHANGELOG.md b/packages/g-plugin-zdog-svg-renderer/CHANGELOG.md index a75d92c5e..84b844e62 100644 --- a/packages/g-plugin-zdog-svg-renderer/CHANGELOG.md +++ b/packages/g-plugin-zdog-svg-renderer/CHANGELOG.md @@ -1,5 +1,13 @@ # @antv/g-plugin-zdog-svg-renderer +## 2.0.7 + +### Patch Changes + +- Updated dependencies [a4d7c7e0] + - @antv/g-plugin-svg-renderer@2.0.7 + - @antv/g-svg@2.0.7 + ## 2.0.6 ### Patch Changes diff --git a/packages/g-plugin-zdog-svg-renderer/package.json b/packages/g-plugin-zdog-svg-renderer/package.json index b79759c8c..0bc4a25a8 100644 --- a/packages/g-plugin-zdog-svg-renderer/package.json +++ b/packages/g-plugin-zdog-svg-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-plugin-zdog-svg-renderer", - "version": "2.0.6", + "version": "2.0.7", "description": "A G plugin of renderer implementation with Zdog", "keywords": [ "antv", diff --git a/packages/g-svg/CHANGELOG.md b/packages/g-svg/CHANGELOG.md index 496553126..186a89f5b 100644 --- a/packages/g-svg/CHANGELOG.md +++ b/packages/g-svg/CHANGELOG.md @@ -1,5 +1,13 @@ # @antv/g-svg +## 2.0.7 + +### Patch Changes + +- Updated dependencies [a4d7c7e0] + - @antv/g-plugin-svg-renderer@2.0.7 + - @antv/g-plugin-svg-picker@2.0.7 + ## 2.0.6 ### Patch Changes diff --git a/packages/g-svg/package.json b/packages/g-svg/package.json index 55194abb0..73b9462cb 100644 --- a/packages/g-svg/package.json +++ b/packages/g-svg/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-svg", - "version": "2.0.6", + "version": "2.0.7", "description": "A renderer implemented by SVG", "keywords": [ "antv", diff --git a/packages/g-web-components/CHANGELOG.md b/packages/g-web-components/CHANGELOG.md index 66c599a8b..ad175d163 100644 --- a/packages/g-web-components/CHANGELOG.md +++ b/packages/g-web-components/CHANGELOG.md @@ -1,5 +1,11 @@ # @antv/g-web-components +## 2.0.9 + +### Patch Changes + +- @antv/g-canvas@2.0.6 + ## 2.0.8 ### Patch Changes diff --git a/packages/g-web-components/package.json b/packages/g-web-components/package.json index fd5b2938c..4ff586ec0 100644 --- a/packages/g-web-components/package.json +++ b/packages/g-web-components/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g-web-components", - "version": "2.0.8", + "version": "2.0.9", "description": "A declarative usage for G implemented with WebComponents", "keywords": [ "antv",