Skip to content

Commit

Permalink
Merge pull request #1551 from VisActor/feat/gif-image
Browse files Browse the repository at this point in the history
feat: add gifImage in vrender-components
  • Loading branch information
Rui-Sun authored Jan 6, 2025
2 parents 63a9a12 + a766717 commit fb57783
Show file tree
Hide file tree
Showing 25 changed files with 460 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vrender-components",
"comment": "feat: add GifImage component",
"type": "none"
}
],
"packageName": "@visactor/vrender-components"
}
12 changes: 12 additions & 0 deletions common/config/rush/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions docs/assets/api/en/common/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ Roboto,Helvetica,Arial,sans-serif, apple color emoji,segoe ui emoji,segoe ui sym
#${prefix} lineThrough(number) = 0

中划线线粗

#${prefix} disableAutoClipedPoptip(boolean) = false

禁用省略hover展示poptip
4 changes: 4 additions & 0 deletions docs/assets/api/zh/common/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ Roboto,Helvetica,Arial,sans-serif, apple color emoji,segoe ui emoji,segoe ui sym
#${prefix} lineThrough(number) = 0

中划线线粗

#${prefix} disableAutoClipedPoptip(boolean) = false

禁用省略hover展示poptip
4 changes: 4 additions & 0 deletions docs/assets/option/en/common/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ Roboto,Helvetica,Arial,sans-serif, apple color emoji,segoe ui emoji,segoe ui sym
#${prefix} lineThrough(number) = 0

中划线线粗

#${prefix} disableAutoClipedPoptip(boolean) = false

禁用省略hover展示poptip
4 changes: 4 additions & 0 deletions docs/assets/option/zh/common/text.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ Roboto,Helvetica,Arial,sans-serif, apple color emoji,segoe ui emoji,segoe ui sym
#${prefix} lineThrough(number) = 0

中划线线粗

#${prefix} disableAutoClipedPoptip(boolean) = false

禁用省略hover展示poptip
3 changes: 2 additions & 1 deletion packages/vrender-core/src/interface/graphic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export type GraphicType =
| 'shadowroot'
| 'polygon'
| 'pyramid3d'
| 'glyph';
| 'glyph'
| string;

// Cursor style
// See: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export class DefaultImageRenderContribution extends DefaultRectRenderContributio
useStyle: boolean = true;
order: number = 0;
drawShape(
rect: any,
image: any,
context: IContext2d,
x: number,
y: number,
Expand All @@ -172,7 +172,7 @@ export class DefaultImageRenderContribution extends DefaultRectRenderContributio
) => boolean
) {
return super.drawShape(
rect,
image,
context,
x,
y,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export class DefaultCanvasImageRender extends BaseRender<IImage> implements IGra

draw(image: IImage, renderService: IRenderService, drawContext: IDrawContext) {
const { image: url } = image.attribute;

if (!url || !image.resources) {
return;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/vrender-core/src/resource-loader/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ export class ResourceLoader {
let data = ResourceLoader.cache.get(url);
if (data) {
// 存在缓存
if (data.loadState === 'init' || data.loadState === 'fail') {
if (data.loadState === 'fail') {
return Promise.reject();
} else if (data.loadState === 'loading') {
} else if (data.loadState === 'init' || data.loadState === 'loading') {
return data.dataPromise.then(data => data.data);
}
return Promise.resolve(data.data);
Expand Down
3 changes: 2 additions & 1 deletion packages/vrender-kits/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"@visactor/vutils": "~0.19.3",
"@visactor/vrender-core": "workspace:0.21.7",
"@resvg/resvg-js": "2.4.1",
"roughjs": "4.5.2"
"roughjs": "4.5.2",
"gifuct-js": "2.1.2"
},
"devDependencies": {
"@internal/bundler": "workspace:*",
Expand Down
3 changes: 3 additions & 0 deletions packages/vrender-kits/src/graphic/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Generator } from '@visactor/vrender-core';

export const GIFIMAGE_NUMBER_TYPE = Generator.GenAutoIncrementId();
156 changes: 156 additions & 0 deletions packages/vrender-kits/src/graphic/gif-image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import type { IImageGraphicAttribute, ISetAttributeContext } from '@visactor/vrender-core';
import { application, Image, ResourceLoader } from '@visactor/vrender-core';
import type { ITimeline } from '@visactor/vrender-core';
import { isString } from '@visactor/vutils';
import type { ParsedFrame } from 'gifuct-js';
import { decompressFrames, parseGIF } from 'gifuct-js';
import type { IGifImage, IGifImageGraphicAttribute } from '../interface/gif-image';
import { GIFIMAGE_NUMBER_TYPE } from './constants';

export class GifImage extends Image implements IGifImage {
type: any = 'gif-image';
declare attribute: IGifImageGraphicAttribute;

frameImageData?: ImageData;
tempCanvas?: HTMLCanvasElement;
tempCtx?: CanvasRenderingContext2D;
gifCanvas?: HTMLCanvasElement;
gifCtx?: CanvasRenderingContext2D;
loadedFrames?: ParsedFrame[];
frameIndex?: number;
playing?: boolean;
lastTime?: number;

constructor(params: IGifImageGraphicAttribute) {
super(params);
this.numberType = GIFIMAGE_NUMBER_TYPE;
this.loadGif();
}

loadGif() {
if (isString(this.attribute.gifImage)) {
ResourceLoader.GetFile(this.attribute.gifImage, 'arrayBuffer')
.then((res: ArrayBuffer) => {
const gif = parseGIF(res);
const frames = decompressFrames(gif, true);
this.renderGIF(frames);
})
.catch(e => {
console.error('Gif load error: ', e);
});
} else if (this.attribute.gifImage instanceof ArrayBuffer) {
const gif = parseGIF(this.attribute.gifImage);
const frames = decompressFrames(gif, true);
this.renderGIF(frames);
}
}

renderGIF(frames: ParsedFrame[]) {
this.loadedFrames = frames;
this.frameIndex = 0;

if (!this.tempCanvas) {
this.tempCanvas = application.global.createCanvas({});
this.tempCtx = this.tempCanvas.getContext('2d');
}

if (!this.gifCanvas) {
this.gifCanvas = application.global.createCanvas({});
this.gifCtx = this.gifCanvas.getContext('2d');
}

this.gifCanvas.width = frames[0].dims.width;
this.gifCanvas.height = frames[0].dims.height;

this.playing = true;
this.lastTime = new Date().getTime();
const animation = this.animate();
if (this.attribute.timeline) {
animation.setTimeline(this.attribute.timeline);
}
animation.to({}, 1000, 'linear').loop(Infinity);
}

renderFrame(context: CanvasRenderingContext2D, x: number, y: number) {
// get the frame
const frame = this.loadedFrames[this.frameIndex || 0];

if (frame.disposalType === 2) {
this.gifCtx.clearRect(0, 0, this.gifCanvas.width, this.gifCanvas.height);
}

// draw image into gifCanvas
this.drawPatch(frame);

// draw gifCanvas into stage
this.manipulate(context, x, y);

// update the frame index
const diff = new Date().getTime() - this.lastTime;
if (frame.delay < diff) {
this.frameIndex++;
this.lastTime = new Date().getTime();
}
if (this.frameIndex >= this.loadedFrames.length) {
this.frameIndex = 0;
}
}

drawPatch(frame: ParsedFrame) {
const dims = frame.dims;

if (
!this.frameImageData ||
dims.width !== this.frameImageData.width ||
dims.height !== this.frameImageData.height
) {
this.tempCanvas.width = dims.width;
this.tempCanvas.height = dims.height;
this.frameImageData = this.tempCtx.createImageData(dims.width, dims.height);
}

// set the patch data as an override
this.frameImageData.data.set(frame.patch);

// draw the patch back over the canvas
this.tempCtx.putImageData(this.frameImageData, 0, 0);

this.gifCtx.drawImage(this.tempCanvas, dims.left, dims.top);
}

manipulate(context: CanvasRenderingContext2D, x: number, y: number) {
context.drawImage(
this.gifCanvas,
0,
0,
this.gifCanvas.width,
this.gifCanvas.height,
x,
y,
this.attribute.width,
this.attribute.height
);
}

setAttribute(key: string, value: any, forceUpdateTag?: boolean, context?: ISetAttributeContext): void {
super.setAttribute(key, value, forceUpdateTag, context);
if (key === 'gifImage') {
this.loadGif();
}
}

setAttributes(
params: Partial<IGifImageGraphicAttribute>,
forceUpdateTag?: boolean,
context?: ISetAttributeContext
): void {
super.setAttributes(params, forceUpdateTag, context);
if (params.gifImage) {
this.loadGif();
}
}
}

export function createGifImage(attributes: IGifImageGraphicAttribute): IGifImage {
return new GifImage(attributes);
}
4 changes: 4 additions & 0 deletions packages/vrender-kits/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export * from './picker/contributions/canvas-picker/arc3d-module';

export * from './picker/contributions/canvas-picker/pyramid3d-module';

export * from './graphic/gif-image';
export * from './picker/contributions/canvas-picker/gif-image-module';
export * from './render/contributions/canvas/gif-image-module';

export * from './register/register-arc';
export * from './register/register-arc3d';
export * from './register/register-area';
Expand Down
19 changes: 19 additions & 0 deletions packages/vrender-kits/src/interface/gif-image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { IGraphic, IImageGraphicAttribute, ITimeline } from '@visactor/vrender-core';
import type { ParsedFrame } from 'gifuct-js';

export interface IGifImageGraphicAttribute extends IImageGraphicAttribute {
timeline?: ITimeline;
gifImage?: string | ArrayBuffer;
}

export interface IGifImage extends IGraphic<IGifImageGraphicAttribute> {
frameImageData?: ImageData;
tempCanvas?: HTMLCanvasElement;
tempCtx?: CanvasRenderingContext2D;
gifCanvas?: HTMLCanvasElement;
gifCtx?: CanvasRenderingContext2D;
loadedFrames?: ParsedFrame[];
frameIndex?: number;
playing?: boolean;
lastTime?: number;
}
18 changes: 17 additions & 1 deletion packages/vrender-kits/src/jsx/jsx-classic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function flatten(list: any, out: any[]): void {
}

export function jsx(type: string | any, config: Record<string, any>, ...children: any) {
const { key, name, id, attribute, stateProxy, ...props } = config || {};
const { key, name, id, attribute, stateProxy, animation, timeline, ...props } = config || {};

let c = type;
if (isString(type)) {
Expand All @@ -29,6 +29,22 @@ export function jsx(type: string | any, config: Record<string, any>, ...children
g.stateProxy = stateProxy;
}

if (name) {
g.name = name;
}

if (isArray(animation)) {
// animation={[
// ['to', { angle: 2 * Math.PI }, 1000, 'linear'],
// ['loop', Infinity]
// ]}
const animate = g.animate();
timeline && animate.setTimeline(timeline);
animation.forEach((item: any[]) => {
animate[item[0]](...item.slice(1));
});
}

return g;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ContainerModule } from '@visactor/vrender-core';
import { CanvasGifImagePicker, CanvasPickerContribution } from '../constants';
import { DefaultCanvasGifImagePicker } from './gif-image-picker';

let loadGifImagePick = false;
export const gifImageCanvasPickModule = new ContainerModule((bind, unbind, isBound, rebind) => {
if (loadGifImagePick) {
return;
}
loadGifImagePick = true;
// gifGifImage picker
bind(CanvasGifImagePicker).to(DefaultCanvasGifImagePicker).inSingletonScope();
bind(CanvasPickerContribution).toService(CanvasGifImagePicker);
});
Loading

0 comments on commit fb57783

Please sign in to comment.