Skip to content

Commit 19995a9

Browse files
committed
feat: add align graphic option
1 parent c036279 commit 19995a9

4 files changed

Lines changed: 94 additions & 5 deletions

File tree

packages/vrender-core/src/graphic/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export const DefaultStyle: IGraphicStyle = {
132132
background: null,
133133
autoAnimateTexture: false,
134134
textureRatio: 1,
135-
textureOptions: null,
135+
textureOptions: { alignToGraphic: true },
136136
backgroundOpacity: 1,
137137
backgroundCornerRadius: 0,
138138
texture: null,

packages/vrender-core/src/interface/graphic.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { IContainPointMode } from '../common/enums';
1212
import type { IFace3d } from './graphic/face3d';
1313
import type { IPickerService } from './picker';
1414
import type { ISymbolClass } from './graphic/symbol';
15+
import type { IContext2d } from './context';
1516

1617
type IStrokeSeg = {
1718
/**
@@ -456,7 +457,69 @@ export type IGraphicStyle = ILayout &
456457
autoAnimateTexture: boolean;
457458
// 如果做动画的话,这里代表ratio
458459
textureRatio: number;
459-
textureOptions: any;
460+
textureOptions: {
461+
/**
462+
* 是否将纹理的 pattern 原点对齐到图元的绘制原点
463+
*/
464+
alignToGraphic?: boolean;
465+
/**
466+
* 纹理在 x 方向的额外偏移量(用户坐标系)
467+
*/
468+
alignOffsetX?: number;
469+
/**
470+
* 纹理在 y 方向的额外偏移量(用户坐标系)
471+
*/
472+
alignOffsetY?: number;
473+
/**
474+
* 是否输出对齐调试信息(仅开发使用)
475+
*/
476+
debugAlign?: boolean;
477+
/**
478+
* 是否使用动态纹理绘制(由调用方提供绘制回调)
479+
*/
480+
dynamicTexture?: (
481+
ctx: IContext2d,
482+
row: number,
483+
column: number,
484+
rowCount: number,
485+
columnCount: number,
486+
ratio: number,
487+
graphic: IGraphic,
488+
width?: number,
489+
height?: number
490+
) => void;
491+
/**
492+
* 动态纹理绘制前回调
493+
*/
494+
beforeDynamicTexture?: (
495+
ctx: IContext2d,
496+
row: number,
497+
column: number,
498+
rowCount: number,
499+
columnCount: number,
500+
ratio: number,
501+
graphic: IGraphic,
502+
width?: number,
503+
height?: number
504+
) => void;
505+
/**
506+
* 动态纹理网格配置
507+
*/
508+
gridConfig?: {
509+
columns?: number;
510+
rows?: number;
511+
gutterColumn?: number;
512+
gutterRow?: number;
513+
};
514+
/**
515+
* 是否使用新 canvas 绘制动态纹理
516+
*/
517+
useNewCanvas?: boolean;
518+
/**
519+
* 其他纹理选项
520+
*/
521+
[key: string]: any;
522+
};
460523
background:
461524
| IBackgroundType
462525
| {

packages/vrender-core/src/render/contributions/render/contributions/base-texture-contribution-render.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,29 @@ export class DefaultBaseTextureRenderContribution implements IBaseRenderContribu
386386

387387
originalContext.restore();
388388
} else if (pattern) {
389+
if (pattern.setTransform) {
390+
const alignToGraphic = !!textureOptions?.alignToGraphic;
391+
const alignOffsetX = textureOptions?.alignOffsetX ?? 0;
392+
const alignOffsetY = textureOptions?.alignOffsetY ?? 0;
393+
let translateX = 0;
394+
let translateY = 0;
395+
if (alignToGraphic) {
396+
// 将 pattern 原点对齐到图形的绘制原点(当前 context 坐标系)
397+
const m = context.currentMatrix;
398+
const e = m?.e ?? 0;
399+
const f = m?.f ?? 0;
400+
// 直接在用户坐标系下对齐(包含 context 的平移)
401+
const ux = e + x + alignOffsetX;
402+
const uy = f + y + alignOffsetY;
403+
translateX = ux;
404+
translateY = uy;
405+
} else if (alignOffsetX || alignOffsetY) {
406+
translateX = alignOffsetX;
407+
translateY = alignOffsetY;
408+
}
409+
// pattern 的 transform 使用用户坐标系单位(不再额外乘 dpr)
410+
pattern.setTransform(new DOMMatrix([1 / context.dpr, 0, 0, 1 / context.dpr, translateX, translateY]));
411+
}
389412
context.highPerformanceSave();
390413
context.setCommonStyle(graphic, graphic.attribute, x, y, graphicAttribute);
391414
context.fillStyle = pattern;

packages/vrender/__tests__/browser/src/pages/texture.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export const page = () => {
88
const shapes = [];
99
const svgTexture = `<svg t="1775469608211" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1306" width="40" height="40"><path d="M478.25 655.5h90.35v237.07h-90.35z" fill="#A5855E" p-id="1307"></path><path d="M568.6 905.07h-90.35c-6.9 0-12.5-5.6-12.5-12.5V655.5c0-6.9 5.6-12.5 12.5-12.5h90.35c6.9 0 12.5 5.6 12.5 12.5v237.07c0 6.9-5.6 12.5-12.5 12.5z m-77.85-25h65.35V668h-65.35v212.07z" p-id="1308"></path><path d="M607.99 905.07H438.87c-6.9 0-12.5-5.6-12.5-12.5s5.6-12.5 12.5-12.5h169.12c6.9 0 12.5 5.6 12.5 12.5s-5.6 12.5-12.5 12.5z" p-id="1309"></path><path d="M523.43 353.39L210.74 749.95l312.69-37.16 312.69 37.16-312.69-396.56z" fill="#ABCF78" p-id="1310"></path><path d="M836.12 762.45c-0.49 0-0.98-0.03-1.48-0.09l-311.21-36.99-311.21 36.99c-5.02 0.6-9.91-1.89-12.38-6.3a12.507 12.507 0 0 1 1.09-13.85l312.69-396.56c2.37-3.01 5.99-4.76 9.82-4.76s7.45 1.75 9.82 4.76l312.69 396.56a12.498 12.498 0 1 1-9.81 20.24z m-312.69-62.16c0.49 0 0.99 0.03 1.48 0.09l282.7 33.6-284.17-360.39-284.17 360.39 282.7-33.6c0.49-0.06 0.98-0.09 1.48-0.09z" p-id="1311"></path><path d="M523.43 277.55l-261 331.01 261-37.17 261 37.17-261-331.01z" fill="#C5E298" p-id="1312"></path><path d="M784.43 621.06c-0.58 0-1.17-0.04-1.77-0.13l-259.24-36.92-259.24 36.92c-5.06 0.72-10.05-1.71-12.6-6.14a12.496 12.496 0 0 1 1.02-13.98l261-331.01c2.37-3.01 5.99-4.76 9.82-4.76s7.45 1.75 9.82 4.76l261 331.01c3.16 4.01 3.57 9.55 1.02 13.98a12.504 12.504 0 0 1-10.83 6.26z m-261-323.32L291.58 591.78l230.09-32.77c1.17-0.17 2.36-0.17 3.52 0l230.09 32.77-231.85-294.04z" p-id="1313"></path><path d="M523.43 210.47L327.93 458.4l195.5-37.17 195.49 37.17-195.49-247.93z" fill="#ABCF78" p-id="1314"></path><path d="M718.92 470.9c-0.77 0-1.56-0.07-2.34-0.22l-193.16-36.72-193.16 36.72c-5.12 0.97-10.31-1.34-13.02-5.79a12.5 12.5 0 0 1 0.87-14.23L513.6 202.73c2.37-3.01 5.99-4.76 9.82-4.76s7.45 1.75 9.82 4.76l195.49 247.93a12.5 12.5 0 0 1 0.87 14.23 12.496 12.496 0 0 1-10.68 6.02z m-195.49-62.17c0.78 0 1.56 0.07 2.33 0.22l162.63 30.92-164.96-209.21-164.96 209.21 162.63-30.92c0.77-0.15 1.55-0.22 2.33-0.22z" p-id="1315"></path><path d="M523.43 151L378.37 334.96l145.06-37.93 145.05 37.93L523.43 151z" fill="#C5E298" p-id="1316"></path><path d="M668.48 347.47c-1.05 0-2.11-0.13-3.16-0.41l-141.9-37.11-141.9 37.11c-5.2 1.36-10.68-0.75-13.62-5.25a12.505 12.505 0 0 1 0.64-14.58L513.6 143.26c2.37-3.01 5.99-4.76 9.82-4.76s7.45 1.75 9.82 4.76L678.3 327.23c3.33 4.22 3.59 10.09 0.64 14.58a12.498 12.498 0 0 1-10.46 5.66z m-145.05-62.94c1.06 0 2.13 0.14 3.16 0.41l109.01 28.51-112.17-142.26-112.17 142.26 109.01-28.51c1.04-0.27 2.1-0.41 3.16-0.41z" p-id="1317"></path></svg>`;
1010
const imgTexture = `https://lf-dp.bytetos.com/obj/dp-open-internet-cn/visactor-site/bytedance/client/img/visactor/navigator-logo.svg`;
11+
const svgTexture2 =
12+
'<svg t="1775564872040" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2430" width="200" height="200"><path d="M546.747475 259.665455c145.454545 18.182465 219.636364 124.36299 219.636363 253.090909 0 33.454545-5.091556 65.454545-16.727919 94.545454l126.545455 73.453899c23.272727-50.907798 34.909091-108.36299 34.909091-167.999353 0-209.454545-146.182465-381.818828-364.364283-400v146.909091z m0 0" fill="#706EE7" p-id="2431"></path><path d="M474.020202 765.847273c-109.090909-18.182465-217.453899-124.364283-217.453899-253.090909 0-128.727919 108.36299-234.908444 217.453899-253.090909V112.756364C292.20202 130.937535 111.838384 303.301818 111.838384 512.756364s180.363636 381.818828 362.181818 400V765.847273z m0 0" fill="#29C287" p-id="2432"></path><path d="M712.565657 670.573899c-40.000646 50.907798-93.090909 85.091556-165.818182 95.273374V912.756364c109.090909-10.909737 224.727919-74.909737 290.182464-168.727273l-124.364282-73.455192z m0 0" fill="#FFC107" p-id="2433"></path></svg>';
1113
shapes.push(
1214
createSymbol({
1315
x: 300,
@@ -63,9 +65,10 @@ export const page = () => {
6365
fill: '#f3f5f7',
6466
stroke: '#333',
6567
lineWidth: 2,
66-
texture: imgTexture,
67-
textureSize: 80,
68-
texturePadding: 6
68+
texture: 'https://api.iconify.design/mdi/smartwatch.svg?width=59.4&height=59.4&color=%23f472b6',
69+
textureOptions: {
70+
alignToGraphic: true
71+
}
6972
})
7073
);
7174

0 commit comments

Comments
 (0)