Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vrender-core",
"comment": "feat: texture support image, make texture clear",
"type": "none"
}
],
"packageName": "@visactor/vrender-core"
}
36 changes: 28 additions & 8 deletions packages/vrender-core/src/graphic/graphic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
{ state: 'init' | 'loading' | 'success' | 'fail'; data?: HTMLImageElement | HTMLCanvasElement }
>;
declare backgroundImg?: boolean;
declare textureImg?: boolean;

declare type: GraphicType;
declare prefixed: string;
Expand Down Expand Up @@ -325,10 +326,14 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
this.valid = this.isValid();
this.updateAABBBoundsStamp = 0;
if (params.background) {
this.loadImage((params.background as any).background ?? params.background, true);
} else if (params.shadowGraphic) {
this.loadImage((params.background as any).background ?? params.background, 'background');
}
if (params.shadowGraphic) {
this.setShadowGraphic(params.shadowGraphic);
}
if (params.texture) {
this.loadImage(params.texture, 'texture');
}
// this.attribute = createTrackableObject(this.attribute);
}

Expand Down Expand Up @@ -695,8 +700,12 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
(this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate(params, this.attribute, null, context)) || params;

if (params.background) {
this.loadImage(params.background, true);
} else if (params.shadowGraphic) {
this.loadImage(params.background, 'background');
}
if (params.texture) {
this.loadImage(params.texture, 'texture');
}
if (params.shadowGraphic) {
this.setShadowGraphic(params.shadowGraphic);
}
this._setAttributes(params, forceUpdateTag, context);
Expand Down Expand Up @@ -743,7 +752,9 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
this._setAttributes(params, forceUpdateTag, context);
}
if (key === 'background') {
this.loadImage(value, true);
this.loadImage(value, 'background');
} else if (key === 'texture') {
this.loadImage(value, 'texture');
} else if (key === 'shadowGraphic') {
this.setShadowGraphic(value);
}
Expand Down Expand Up @@ -776,8 +787,12 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
(this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate(params, this.attribute, null, context)) || params;
this.attribute = params;
if (params.background) {
this.loadImage(params.background, true);
} else if (params.shadowGraphic) {
this.loadImage(params.background, 'background');
}
if (params.texture) {
this.loadImage(params.texture, 'texture');
}
if (params.shadowGraphic) {
this.setShadowGraphic(params.shadowGraphic);
}
this._updateTag = UpdateTag.INIT;
Expand Down Expand Up @@ -1460,7 +1475,9 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
return this.pathProxy;
}

loadImage(image: any, background: boolean = false) {
loadImage(image: any, type?: 'background' | 'texture') {
const background = type === 'background';
const texture = type === 'texture';
if (!image || (background && backgroundNotImage(image))) {
return;
}
Expand All @@ -1482,13 +1499,16 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
// TODO 封装isSvg到@visactor/vutils
ResourceLoader.GetSvg(image, this);
this.backgroundImg = this.backgroundImg || background;
this.textureImg = this.textureImg || texture;
} else if (isValidUrl(image) || image.includes('/') || isBase64(image)) {
ResourceLoader.GetImage(image, this);
this.backgroundImg = this.backgroundImg || background;
this.textureImg = this.textureImg || texture;
}
} else if (isObject(image)) {
(cache.state = 'success'), (cache.data = image);
this.backgroundImg = this.backgroundImg || background;
this.textureImg = this.textureImg || texture;
} else {
cache.state = 'fail';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
IThemeAttribute
} from '../../../../interface';
import { pi2 } from '@visactor/vutils';
import { ResourceLoader } from '../../../../resource-loader/loader';

function formatRatio(ratio: number) {
if (ratio <= 0.5) {
Expand Down Expand Up @@ -81,7 +82,7 @@ export class DefaultBaseTextureRenderContribution implements IBaseRenderContribu
cb: (r: number, targetContext: IContext2d) => void
) {
const r = (size - padding * 2) / 2;
const dpr = targetContext.dpr;
const dpr = targetContext.dpr * 2;
const canvas = canvasAllocate.allocate({ width: size, height: size, dpr });
const ctx = canvas.getContext('2d');
if (!ctx) {
Expand Down Expand Up @@ -247,36 +248,6 @@ export class DefaultBaseTextureRenderContribution implements IBaseRenderContribu
texturePadding: number
) {
const { textureRatio = graphicAttribute.textureRatio, textureOptions = null } = graphic.attribute;
let pattern: CanvasPattern = this.textureMap.get(texture);

if (!pattern) {
switch (texture) {
case 'circle':
pattern = this.createCirclePattern(textureSize, texturePadding, textureColor, context);
break;
case 'diamond':
pattern = this.createDiamondPattern(textureSize, texturePadding, textureColor, context);
break;
case 'rect':
pattern = this.createRectPattern(textureSize, texturePadding, textureColor, context);
break;
case 'vertical-line':
pattern = this.createVerticalLinePattern(textureSize, texturePadding, textureColor, context);
break;
case 'horizontal-line':
pattern = this.createHorizontalLinePattern(textureSize, texturePadding, textureColor, context);
break;
case 'bias-lr':
pattern = this.createBiasLRLinePattern(textureSize, texturePadding, textureColor, context);
break;
case 'bias-rl':
pattern = this.createBiasRLLinePattern(textureSize, texturePadding, textureColor, context);
break;
case 'grid':
pattern = this.createGridPattern(textureSize, texturePadding, textureColor, context);
break;
}
}

if (textureOptions && textureOptions.dynamicTexture) {
// 动态纹理
Expand Down Expand Up @@ -373,12 +344,8 @@ export class DefaultBaseTextureRenderContribution implements IBaseRenderContribu
}

originalContext.restore();
} else if (pattern) {
context.highPerformanceSave();
context.setCommonStyle(graphic, graphic.attribute, x, y, graphicAttribute);
context.fillStyle = pattern;
context.fill();
context.highPerformanceRestore();

return;
} else if (texture === 'wave') {
context.save();
context.setCommonStyle(graphic, graphic.attribute, x, y, graphicAttribute);
Expand All @@ -394,6 +361,86 @@ export class DefaultBaseTextureRenderContribution implements IBaseRenderContribu
y + b.y1 - y
);
context.restore();
return;
}

let pattern: CanvasPattern = this.textureMap.get(texture);

if (!pattern) {
switch (texture) {
case 'circle':
pattern = this.createCirclePattern(textureSize, texturePadding, textureColor, context);
break;
case 'diamond':
pattern = this.createDiamondPattern(textureSize, texturePadding, textureColor, context);
break;
case 'rect':
pattern = this.createRectPattern(textureSize, texturePadding, textureColor, context);
break;
case 'vertical-line':
pattern = this.createVerticalLinePattern(textureSize, texturePadding, textureColor, context);
break;
case 'horizontal-line':
pattern = this.createHorizontalLinePattern(textureSize, texturePadding, textureColor, context);
break;
case 'bias-lr':
pattern = this.createBiasLRLinePattern(textureSize, texturePadding, textureColor, context);
break;
case 'bias-rl':
pattern = this.createBiasRLLinePattern(textureSize, texturePadding, textureColor, context);
break;
case 'grid':
pattern = this.createGridPattern(textureSize, texturePadding, textureColor, context);
break;
}
if (!pattern && graphic.textureImg) {
const res = (graphic as any).resources?.get(texture);
if (res && res.state === 'success' && res.data) {
const image = res.data as HTMLImageElement;

// 创建临时canvas来处理textureSize和texturePadding
const dpr = context.dpr * 2;
const canvas = canvasAllocate.allocate({ width: textureSize, height: textureSize, dpr });
const ctx = canvas.getContext('2d');
if (!ctx) {
return;
}
ctx.inuse = true;
ctx.clearMatrix();
ctx.setTransformForCurrent(true);
ctx.clearRect(0, 0, textureSize, textureSize);

// 计算图片绘制区域(考虑padding)
const imageSize = textureSize - texturePadding * 2;
const imageX = texturePadding;
const imageY = texturePadding;

// 在临时canvas上绘制调整大小的图片
ctx.drawImage(image, imageX, imageY, imageSize, imageSize);

// 使用临时canvas创建pattern
pattern = context.createPattern(canvas.nativeCanvas, 'repeat');
if (pattern && pattern.setTransform) {
pattern.setTransform(new DOMMatrix([1 / dpr, 0, 0, 1 / dpr, 0, 0]));
}

// 释放临时canvas
canvasAllocate.free(canvas);

// 缓存创建的pattern以提高性能
if (pattern) {
this.textureMap.set(texture, pattern);
}
}
}
}

if (pattern) {
context.highPerformanceSave();
context.setCommonStyle(graphic, graphic.attribute, x, y, graphicAttribute);
context.fillStyle = pattern;
context.fill();
context.highPerformanceRestore();
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions packages/vrender-core/src/resource-loader/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ export class ResourceLoader {
private static toLoadAueue: { url: string; marks: ImagePayload[] }[] = [];
private static onLoadSuccessCb: (() => void)[] = [];

static GetImageFromCache(url: string, mark: ImagePayload) {
const data = ResourceLoader.cache.get(url);
if (data) {
return data;
}
if (url.startsWith('<svg')) {
ResourceLoader.GetSvg(url, mark);
} else {
ResourceLoader.GetImage(url, mark);
}
return null;
}

static GetImage(url: string, mark: ImagePayload) {
const data = ResourceLoader.cache.get(url);
if (data) {
Expand Down
Loading