Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/image stroke #1747

Merged
merged 3 commits into from
Feb 21, 2025
Merged
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: graphic support clipConfig attribute",
"type": "none"
}
],
"packageName": "@visactor/vrender-core"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vrender-core",
"comment": "feat: image support auto width height by rawImage wh",
"type": "none"
}
],
"packageName": "@visactor/vrender-core"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vrender-core",
"comment": "fix: fix issue with image stroke style",
"type": "none"
}
],
"packageName": "@visactor/vrender-core"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vrender-kits",
"comment": "feat: graphic support clipConfig attribute",
"type": "none"
}
],
"packageName": "@visactor/vrender-kits"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vrender",
"comment": "feat: image support auto width height by rawImage wh",
"type": "none"
}
],
"packageName": "@visactor/vrender"
}
3 changes: 3 additions & 0 deletions packages/vrender-core/src/graphic/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ export const DefaultAttribute: Required<IGraphicAttribute> = {
overflow: 'hidden',
shadowPickMode: 'graphic',
keepStrokeScale: false,
clipConfig: null,
...DefaultDebugAttribute,
...DefaultStyle,
...DefaultTransform
Expand Down Expand Up @@ -370,6 +371,8 @@ export const DefaultImageAttribute: Required<IImageGraphicAttribute> = {
image: '',
width: 0,
height: 0,
maxWidth: 500,
maxHeight: 500,
...DefaultAttribute,
fill: true,
cornerRadius: 0,
Expand Down
99 changes: 96 additions & 3 deletions packages/vrender-core/src/graphic/graphic.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ICustomPath2D } from './../interface/path';
import type { Dict, IPointLike, IAABBBounds, IOBBBounds } from '@visactor/vutils';
import { isArray, OBBBounds } from '@visactor/vutils';
import { isArray, max, OBBBounds } from '@visactor/vutils';
import {
AABBBounds,
Matrix,
Expand Down Expand Up @@ -35,7 +35,8 @@ import type {
IShadowRoot,
IStage,
IStep,
ISubAnimate
ISubAnimate,
ISymbolClass
} from '../interface';
import { EventTarget, CustomEvent } from '../event';
import { DefaultTransform } from './config';
Expand All @@ -48,7 +49,11 @@ import { AttributeUpdateType, IContainPointMode, UpdateTag } from '../common/enu
import { BoundsContext } from '../common/bounds-context';
import { renderCommandList } from '../common/render-command-list';
import { parsePadding } from '../common/utils';
import { builtinSymbolsMap, builtInSymbolStrMap, CustomSymbolClass } from './builtin-symbol';
import { isSvg, XMLParser } from '../common/xml';
import { SVG_PARSE_ATTRIBUTE_MAP, SVG_PARSE_ATTRIBUTE_MAP_KEYS } from './constants';

const _tempBounds = new AABBBounds();
/**
* pathProxy参考自zrender
* BSD 3-Clause License
Expand Down Expand Up @@ -187,6 +192,8 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr

declare _events?: any;

static userSymbolMap: Record<string, ISymbolClass> = {};

declare onBeforeAttributeUpdate?: (
val: any,
attributes: Partial<T>,
Expand All @@ -196,7 +203,7 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
declare parent: any;

declare resources?: Map<
string,
string | HTMLImageElement | HTMLCanvasElement,
{ state: 'init' | 'loading' | 'success' | 'fail'; data?: HTMLImageElement | HTMLCanvasElement }
>;
declare backgroundImg?: boolean;
Expand Down Expand Up @@ -270,6 +277,7 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
declare attachedThemeGraphic?: IGraphic;
protected updateAABBBoundsStamp: number;
protected updateOBBBoundsStamp?: number;
declare clipPathMap?: Map<string, ISymbolClass>;

constructor(params: T = {} as T) {
super();
Expand Down Expand Up @@ -376,6 +384,91 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
full?: boolean
): IAABBBounds;

getClipPath() {
const { clipConfig } = this.attribute;
if (!clipConfig) {
return null;
}
if (!this.clipPathMap) {
this.clipPathMap = new Map();
}

const { shape } = clipConfig;
let path = this.clipPathMap.get(shape) || null;
if (!path) {
// 即使清理,避免内存溢出
if (this.clipPathMap.size > 10) {
this.clipPathMap.clear();
}
path = this.parsePath(shape);
path && this.clipPathMap.set(shape, path);
}
return path;
}

parsePath(symbolType: string) {
if (!symbolType) {
return null;
}
let path = builtinSymbolsMap[symbolType];
if (path) {
return path;
}
path = Graphic.userSymbolMap[symbolType];
if (path) {
return path;
}
const _symbolType = builtInSymbolStrMap[symbolType];
symbolType = _symbolType || symbolType;
const valid = isSvg(symbolType);
if (valid === true) {
const parser = new XMLParser();
const { svg } = parser.parse(symbolType);
if (!svg) {
return null;
}
const path = isArray(svg.path) ? svg.path : [svg.path];
_tempBounds.clear();
const cacheList: { path: CustomPath2D; attribute: Record<string, any> }[] = [];
path.forEach((item: any) => {
const cache = new CustomPath2D().fromString(item.d);
const attribute: any = {};
SVG_PARSE_ATTRIBUTE_MAP_KEYS.forEach(k => {
if (item[k]) {
(attribute as any)[(SVG_PARSE_ATTRIBUTE_MAP as any)[k]] = item[k];
}
});
// 查找
cacheList.push({
path: cache,
attribute
});
_tempBounds.union(cache.bounds);
});
const width = _tempBounds.width();
const height = _tempBounds.height();
// 规范化到1
const maxWH = max(width, height);
const scale = 1 / maxWH;
cacheList.forEach(cache => cache.path.transform(0, 0, scale, scale));

const _parsedPath = new CustomSymbolClass(symbolType, cacheList, true);
Graphic.userSymbolMap[symbolType] = _parsedPath;
return _parsedPath;
}

const cache = new CustomPath2D().fromString(symbolType);
const width = cache.bounds.width();
const height = cache.bounds.height();
// 规范化到1
const maxWH = max(width, height);
const scale = 1 / maxWH;
cache.transform(0, 0, scale, scale);
const _parsedPath = new CustomSymbolClass(symbolType, cache);
Graphic.userSymbolMap[symbolType] = _parsedPath;
return _parsedPath;
}

protected doUpdateAABBBounds(full?: boolean): IAABBBounds {
this.updateAABBBoundsStamp++;
const graphicTheme = this.getGraphicTheme();
Expand Down
79 changes: 63 additions & 16 deletions packages/vrender-core/src/graphic/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export class Image extends Graphic<IImageGraphicAttribute> implements IImage {
// 资源加载完成后回调,外部通过回调获取图片资源尺寸
successCallback?: () => void;
failCallback?: () => void;
declare _actualWidth?: number;
declare _actualHeight?: number;

static NOWORK_ANIMATE_ATTR = {
image: 1,
Expand All @@ -33,25 +35,39 @@ export class Image extends Graphic<IImageGraphicAttribute> implements IImage {
this.loadImage(this.attribute.image);
}

get width(): number {
return this.attribute.width ?? 0;
}
set width(width: number) {
if (this.attribute.width === width) {
this.attribute.width = width;
this.addUpdateShapeAndBoundsTag();
getImageElement(): HTMLImageElement | HTMLCanvasElement | null {
const { image } = this.attribute;
if (!image || !this.resources) {
return null;
}
const res = this.resources.get(image);
if (res.state !== 'success') {
return null;
}
return res.data;
}

get height(): number {
return this.attribute.height ?? 0;
}
set height(height: number) {
if (this.attribute.height === height) {
this.attribute.height = height;
this.addUpdateShapeAndBoundsTag();
}
get width(): number {
this.tryUpdateAABBBounds();
return this._actualWidth;
}
// set width(width: number) {
// if (this.attribute.width === width) {
// this.attribute.width = width;
// this.addUpdateShapeAndBoundsTag();
// }
// }

get height(): number {
this.tryUpdateAABBBounds();
return this._actualHeight;
}
// set height(height: number) {
// if (this.attribute.height === height) {
// this.attribute.height = height;
// this.addUpdateShapeAndBoundsTag();
// }
// }
get repeatX(): IRepeatType {
return this.attribute.repeatX ?? 'no-repeat';
}
Expand Down Expand Up @@ -85,6 +101,7 @@ export class Image extends Graphic<IImageGraphicAttribute> implements IImage {
this.successCallback();
}
});
this.addUpdateBoundTag();
}

imageLoadFail(url: string, cb?: () => void): void {
Expand Down Expand Up @@ -123,7 +140,37 @@ export class Image extends Graphic<IImageGraphicAttribute> implements IImage {
aabbBounds: IAABBBounds
) {
if (!this.updatePathProxyAABBBounds(aabbBounds)) {
const { width = imageTheme.width, height = imageTheme.height } = attribute;
const { maxWidth = imageTheme.maxWidth, maxHeight = imageTheme.maxHeight } = attribute;
let { width, height } = attribute;
if (width == null || height == null) {
const imageElement = this.getImageElement();
if (imageElement) {
// 如果给了width或者height,那就使用已给的width或者height来按比例缩放,否则就在maxWidth和maxHeight之间按比例缩放
const imageWidth = imageElement.width;
const imageHeight = imageElement.height;
if (width != null) {
height = width * (imageHeight / imageWidth);
} else if (height != null) {
width = height * (imageWidth / imageHeight);
} else {
// 如果width和height都没有给,那就使用maxWidth和maxHeight来按比例缩放
const imageRatio = imageWidth / imageHeight;
const maxWHRatio = maxWidth / maxHeight;
if (imageRatio > maxWHRatio) {
width = maxWidth;
height = maxWidth / imageRatio;
} else {
height = maxHeight;
width = maxHeight * imageRatio;
}
}
} else {
width = maxWidth;
height = maxHeight;
}
}
this._actualWidth = width;
this._actualHeight = height;
aabbBounds.set(0, 0, width, height);
}

Expand Down
Loading
Loading