diff --git a/docs/assets/examples/en/storytelling/disappear-animation-gaussion-blur.md b/docs/assets/examples/en/storytelling/disappear-animation-gaussion-blur.md new file mode 100644 index 0000000000..82367bf32d --- /dev/null +++ b/docs/assets/examples/en/storytelling/disappear-animation-gaussion-blur.md @@ -0,0 +1,124 @@ +--- +category: examples +group: storytelling +title: Disappear Animation Gaussian Blur +keywords: animation,morphing,bar,pie,barChart,pieChart,comparison +order: 42-0 +cover: /vchart/preview/disappear-animation-gaussian-blur-2.0.11.gif +option: lineChart#animationAppear +--- + +# Disappear Animation Gaussian Blur Effect + +In custom animation effects, you can implement the Gaussian blur effect by using the `afterStageRender` method. + +## Key configuration + +- callBack: The custom callback function or animation implementation class for the disappear animation, used to execute user-defined disappear effects. + +### Demo source + +```javascript livedemo +// import { vglobal } from '@visactor/vchart'; +const vglobal = VCHART_MODULE.vglobal; + +function applyDownsampleBlur(imageData, radius) { + const { width, height } = imageData; + + const downsample = Math.max(1, Math.floor(radius / 2)); + const smallWidth = Math.floor(width / downsample); + const smallHeight = Math.floor(height / downsample); + + const tempCanvas = vglobal.createCanvas({ + width: smallWidth, + height: smallHeight, + dpr: 1 + }); + const tempCtx = tempCanvas.getContext('2d'); + if (!tempCtx) { + return imageData; + } + + const originalCanvas = vglobal.createCanvas({ + width: width, + height: height, + dpr: 1 + }); + const originalCtx = originalCanvas.getContext('2d'); + if (!originalCtx) { + return imageData; + } + + originalCtx.putImageData(imageData, 0, 0); + + tempCtx.drawImage(originalCanvas, 0, 0, smallWidth, smallHeight); + + tempCtx.filter = `blur(${radius / downsample}px)`; + tempCtx.drawImage(tempCanvas, 0, 0); + tempCtx.filter = 'none'; + + originalCtx.clearRect(0, 0, width, height); + originalCtx.drawImage(tempCanvas, 0, 0, width, height); + + return originalCtx.getImageData(0, 0, width, height); +} + +function gaussianBlurAnimate(canvas, ratio) { + const blurRadius = ratio * 8; + + if (blurRadius <= 0) { + return canvas; + } + + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; + } + + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvas, 0, 0); + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const blurredImageData = applyDownsampleBlur(imageData, blurRadius); + ctx.putImageData(blurredImageData, 0, 0); + + ctx.fillStyle = `rgba(255, 255, 255, ${ratio.toFixed(2)})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + return c; +} + +const spec = { + type: 'bar', + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + xField: 'month', + yField: 'sales', + animationDisappear: { + callBack: (stage, canvas, ratio) => gaussianBlurAnimate(canvas, ratio), + duration: 2000 + } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderAsync(); + +setInterval(() => { + vchart.runDisappearAnimation(); +}, 4000); +``` diff --git a/docs/assets/examples/en/storytelling/disappear-animation-wipe.md b/docs/assets/examples/en/storytelling/disappear-animation-wipe.md new file mode 100644 index 0000000000..dd0ab7ad57 --- /dev/null +++ b/docs/assets/examples/en/storytelling/disappear-animation-wipe.md @@ -0,0 +1,100 @@ +--- +category: examples +group: storytelling +title: Disappear Animation Wipe +keywords: animation,morphing,bar,pie,barChart,pieChart,comparison +order: 42-0 +cover: /vchart/preview/disappear-animation-wipe-2.0.11.gif +option: lineChart#animationAppear +--- + +# Disappear Animation Wipe Effect + +In custom animation effects, you can implement the wipe animation effect by using the `afterStageRender` method. + +## Key configuration + +- callBack: The custom callback function or animation implementation class for the disappear animation, used to execute user-defined disappear effects. + +### Demo source + +```javascript livedemo +// import { vglobal } from '@visactor/vchart'; +const vglobal = VCHART_MODULE.vglobal; + +function wipeAnimate(canvas, ratio) { + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; + } + + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvas, 0, 0); + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const data = imageData.data; + + const wipePosition = Math.floor(canvas.width * ratio); + + const gradientWidth = Math.min(canvas.width * 0.3, 50); + + for (let y = 0; y < canvas.height; y++) { + for (let x = 0; x < canvas.width; x++) { + const index = (y * canvas.width + x) * 4; + const originalAlpha = data[index + 3]; + const distance = x - wipePosition; + + let newAlpha; + if (distance <= 0) { + newAlpha = 0; + } else if (distance <= gradientWidth) { + const gradientRatio = distance / gradientWidth; + newAlpha = Math.floor(originalAlpha * gradientRatio); + } else { + newAlpha = originalAlpha; + } + + data[index + 3] = newAlpha; + } + } + + ctx.putImageData(imageData, 0, 0); + + return c; +} + +const spec = { + type: 'bar', + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + xField: 'month', + yField: 'sales', + animationDisappear: { + callBack: (stage, canvas, ratio) => wipeAnimate(canvas, ratio), + easing: 'linear', + duration: 2000 + } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderAsync(); + +setInterval(() => { + vchart.runDisappearAnimation(); +}, 3000); +``` diff --git a/docs/assets/examples/en/storytelling/disappear-animation.md b/docs/assets/examples/en/storytelling/disappear-animation.md new file mode 100644 index 0000000000..7dd173d0f0 --- /dev/null +++ b/docs/assets/examples/en/storytelling/disappear-animation.md @@ -0,0 +1,101 @@ +--- +category: examples +group: storytelling +title: Disappear Animation +keywords: animation,morphing,bar,pie,barChart,pieChart,comparison +order: 42-0 +cover: /vchart/preview/disappear-animation-2.0.11.gif +option: lineChart#animationAppear +--- + +# Disappear Animation + +User can manually call `vchart.runDisappearAnimation()` method to execute the disappear animation. +The difference between the disappear animation and other animations is that it provides an additional `callBack` parameter to execute user-defined disappear effects. +User can configure the custom interpolation callback function or provide a custom animation implementation class in the `callBack` parameter. + +## Key configuration + +- callBack: The custom callback function or animation implementation class for the disappear animation, used to execute user-defined disappear effects. + +### Demo source + +```javascript livedemo +// import { vglobal } from '@visactor/vchart'; +// import { AStageAnimate } from '@visactor/vrender-animate'; +const vglobal = VCHART_MODULE.vglobal; +const AStageAnimate = VRender.AStageAnimate; + +// custom disappear animation implementation class +class TestStageAnimate extends AStageAnimate { + onUpdate(end, ratio, out) { + super.onUpdate(end, ratio, out); + this.ratio = ratio; + } + + // update the canvas rendering content + afterStageRender(stage, canvas) { + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; + } + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvas, 0, 0); + ctx.fillStyle = `rgba(255, 255, 255, ${this.ratio.toFixed(2)})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + return c; + } +} + +const spec = { + type: 'bar', + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + xField: 'month', + yField: 'sales', + animationDisappear: { + // custom disappear animation callback function + callBack: (stage, canvas, ratio) => { + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; + } + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvas, 0, 0); + ctx.fillStyle = `rgba(255, 255, 255, ${ratio.toFixed(2)})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + return c; + }, + // custom disappear animation class usage + // callBack: TestStageAnimate, + duration: 1000 + } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderAsync(); + +setInterval(() => { + vchart.runDisappearAnimation(); +}, 2000); +``` diff --git a/docs/assets/examples/menu.json b/docs/assets/examples/menu.json index aca0bb2d3a..3e719af8f5 100644 --- a/docs/assets/examples/menu.json +++ b/docs/assets/examples/menu.json @@ -126,6 +126,27 @@ "zh": "柱状图、散点图间切换的全局动画", "en": "Global animation for switching between bar charts and scatter plots" } + }, + { + "path": "disappear-animation", + "title": { + "zh": "退场动画", + "en": "Disappear Animation" + } + }, + { + "path": "disappear-animation-gaussion-blur", + "title": { + "zh": "退场动画高斯模糊效果", + "en": "Disappear Animation Gaussian Blur Effect" + } + }, + { + "path": "disappear-animation-wipe", + "title": { + "zh": "退场动画擦除效果", + "en": "Disappear Animation Wipe Effect" + } } ] }, @@ -3295,4 +3316,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/docs/assets/examples/zh/storytelling/disappear-animation-dissolve-config.md b/docs/assets/examples/zh/storytelling/disappear-animation-dissolve-config.md deleted file mode 100644 index 51b1156772..0000000000 --- a/docs/assets/examples/zh/storytelling/disappear-animation-dissolve-config.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -category: examples -group: storytelling -title: 溶解退场动画特效配置 -keywords: animation,disappear,particle,distortion,blur,glitch,pixelation,dissolve,stage -order: 42-20 -cover: -option: commonChart#animationDisappear ---- - -# 溶解退场动画特效配置 - -VRender 提供了丰富的阶段动画特效,包括粒子分解、扭曲变形、模糊效果、故障艺术、像素化、颜色效果和溶解效果等。这些动画可以通过 `animationDisappear.stage` 配置来应用(也可以尝试应用在`animationAppear.stage`等动画阶段)。 - -其中,溶解效果,根据方向不同,还可以细分为向内溶解、向外溶解、径向溶解、水平溶解、垂直溶解等。 - -## 关键配置 - -- `animationDisappear.stage` 用于配置舞台级别的退场动画特效 - - `type`: 动画类型,可选值为 - - `dissolve`: 溶解, - - `duration`: 动画时长(毫秒) - - `easing`: 缓动函数 - - `options`: 特效配置参数 - - `dissolveType`: 溶解效果类型,默认值为 `outward` - - 'outward': 向内溶解 - 从边缘开始向内溶解 - - 'inward': 向外溶解 - 从中心开始向外溶解 - - 'radial': 径向溶解 - 按角度顺序溶解 - - 'leftToRight': 从左侧开始向右溶解 - - 'rightToLeft': 从右侧开始向左溶解 - - 'topToBottom': 从顶部开始向下溶解 - - 'bottomToTop': 从底部开始向上溶解 - - `useWebGL`: 是否使用WebGL实现 (true: GPU加速, false: CPU计算),默认值为 `true` - - `noiseScale`: 溶解颗粒大小(0: 平滑溶解无颗粒, 1-20: 颗粒大小像素值,值越大颗粒越大),默认值为 `8` - - `fadeEdge`: 是否启用边缘渐变,让溶解效果更自然,默认值为 `true` - -### 代码演示 - -```javascript livedemo -const spec = { - type: 'funnel', - data: { - values: [ - { name: '展示', value: 100 }, - { name: '点击', value: 80 }, - { name: '访问', value: 60 }, - { name: '咨询', value: 40 }, - { name: '订单', value: 20 } - ] - }, - categoryField: 'name', - valueField: 'value', - animationAppear: { - stage: { - type: 'dissolve', - duration: 5000, - easing: 'linear', - options: { - dissolveType: 'topToBottom', - useWebGL: true, - noiseScale: 5, - fadeEdge: false - } - } - }, - animationUpdate: { - duration: 300 - }, - animationEnter: { - duration: 300 - } -}; - -const vchart = new VChart(spec, { dom: CONTAINER_ID }); -vchart.renderAsync(); -``` - -## 相关教程 - -[VChart动画教程](/vchart/guide/tutorial_docs/Animation/Animation_Types) diff --git a/docs/assets/examples/zh/storytelling/disappear-animation-gaussion-blur.md b/docs/assets/examples/zh/storytelling/disappear-animation-gaussion-blur.md new file mode 100644 index 0000000000..c85db66855 --- /dev/null +++ b/docs/assets/examples/zh/storytelling/disappear-animation-gaussion-blur.md @@ -0,0 +1,141 @@ +--- +category: examples +group: storytelling +title: Disappear Animation Gaussian Blur +keywords: animation,morphing,bar,pie,barChart,pieChart,comparison +order: 42-0 +cover: /vchart/preview/disappear-animation-gaussian-blur-2.0.11.gif +option: lineChart#animationAppear +--- + +# 退场动画高斯模糊效果 + +在自定义动画效果中可以通过 `afterStageRender` 方法实现高斯模糊效果。 + +## 关键配置 + +- callBack: 退场动画的自定义回调函数/动画实现类,用于执行用户自定义的退场效果。 + +### 代码演示 + +```javascript livedemo +// import { vglobal } from '@visactor/vchart'; +const vglobal = VCHART_MODULE.vglobal; + +function applyDownsampleBlur(imageData, radius) { + const { width, height } = imageData; + + // 降采样因子,减少计算量 + const downsample = Math.max(1, Math.floor(radius / 2)); + const smallWidth = Math.floor(width / downsample); + const smallHeight = Math.floor(height / downsample); + + // 创建小尺寸的临时canvas + const tempCanvas = vglobal.createCanvas({ + width: smallWidth, + height: smallHeight, + dpr: 1 + }); + const tempCtx = tempCanvas.getContext('2d'); + if (!tempCtx) { + return imageData; + } + + // 将图像绘制到小canvas上 + const originalCanvas = vglobal.createCanvas({ + width: width, + height: height, + dpr: 1 + }); + const originalCtx = originalCanvas.getContext('2d'); + if (!originalCtx) { + return imageData; + } + + originalCtx.putImageData(imageData, 0, 0); + + // 缩小绘制(自动插值) + tempCtx.drawImage(originalCanvas, 0, 0, smallWidth, smallHeight); + + // 应用模糊到小图像 + tempCtx.filter = `blur(${radius / downsample}px)`; + tempCtx.drawImage(tempCanvas, 0, 0); + tempCtx.filter = 'none'; + + // 放大回原尺寸 + originalCtx.clearRect(0, 0, width, height); + originalCtx.drawImage(tempCanvas, 0, 0, width, height); + + return originalCtx.getImageData(0, 0, width, height); +} + +function gaussianBlurAnimate(canvas, ratio) { + const blurRadius = ratio * 8; + + // 如果模糊强度为0,直接返回原图 + if (blurRadius <= 0) { + return canvas; + } + + // 使用传统的像素级模糊 + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; + } + + // 清空画布 + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // 绘制原始图像 + ctx.drawImage(canvas, 0, 0); + + // 获取图像数据 + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + + // 高质量模式下统一使用降采样模糊 + const blurredImageData = applyDownsampleBlur(imageData, blurRadius); + + // 将模糊后的图像数据绘制到画布上 + ctx.putImageData(blurredImageData, 0, 0); + + // 添加白色遮罩层 + ctx.fillStyle = `rgba(255, 255, 255, ${ratio.toFixed(2)})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + return c; +} + +const spec = { + type: 'bar', + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + xField: 'month', + yField: 'sales', + animationDisappear: { + callBack: (stage, canvas, ratio) => gaussianBlurAnimate(canvas, ratio), + duration: 2000 + } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderAsync(); + +setInterval(() => { + vchart.runDisappearAnimation(); +}, 4000); +``` diff --git a/docs/assets/examples/zh/storytelling/disappear-animation-glitch-config.md b/docs/assets/examples/zh/storytelling/disappear-animation-glitch-config.md deleted file mode 100644 index c7e97643c3..0000000000 --- a/docs/assets/examples/zh/storytelling/disappear-animation-glitch-config.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -category: examples -group: storytelling -title: 故障退场动画特效配置 -keywords: animation,disappear,particle,distortion,blur,glitch,pixelation,dissolve,stage -order: 42-20 -cover: -option: commonChart#animationDisappear ---- - -# 故障退场动画特效配置 - -VRender 提供了丰富的阶段动画特效,包括粒子分解、扭曲变形、模糊效果、故障艺术、像素化、颜色效果和溶解效果等。这些动画可以通过 `animationDisappear.stage` 配置来应用(也可以尝试应用在`animationAppear.stage`等动画阶段)。 - -其中,故障效果支持 'rgb-shift'、'digital-distortion'、'scan-lines'、'data-corruption' 四种类型 - -## 关键配置 - -- `animationDisappear.stage` 用于配置舞台级别的退场动画特效 - - `type`: 动画类型 - - `glitch`: 故障, - - `duration`: 动画时长(毫秒) - - `easing`: 缓动函数 - - `options`: 特效配置参数 - - `effectType`: 特效类型,默认值为 `rgb-shift` - - `rgb-shift`: RGB 偏移, - - `digital-distortion`: 数字失真, - - `scan-lines`: 扫描线, - - `data-corruption`: 数据损坏, - - `intensity`: 特效强度,范围为 0 到 1,默认值为 0.5 - - -### 代码演示 - -```javascript livedemo -const spec = { - type: 'bar', - data: { - values: [ - { type: 'Nail polish', country: 'Africa', value: 4229 }, - { type: 'Nail polish', country: 'EU', value: 4376 }, - { type: 'Nail polish', country: 'China', value: 3054 }, - { type: 'Nail polish', country: 'USA', value: 12814 }, - { type: 'Eyebrow pencil', country: 'Africa', value: 3932 }, - { type: 'Eyebrow pencil', country: 'EU', value: 3987 }, - { type: 'Eyebrow pencil', country: 'China', value: 5067 }, - { type: 'Eyebrow pencil', country: 'USA', value: 13012 }, - { type: 'Rouge', country: 'Africa', value: 5221 }, - { type: 'Rouge', country: 'EU', value: 3574 }, - { type: 'Rouge', country: 'China', value: 7004 }, - { type: 'Rouge', country: 'USA', value: 11624 }, - { type: 'Lipstick', country: 'Africa', value: 9256 }, - { type: 'Lipstick', country: 'EU', value: 4376 }, - { type: 'Lipstick', country: 'China', value: 9054 }, - { type: 'Lipstick', country: 'USA', value: 8814 } - ] - }, - xField: 'type', - yField: 'value', - seriesField: 'country', - animationAppear: { - stage: { - type: 'glitch', - duration: 3000, - easing: 'linear', - options: { - effectType: 'digital-distortion', - intensity: 1 - } - }, - type: 'fadeOut', - duration: 3000, - easing: 'linear' - }, - animationUpdate: { - duration: 300 - }, - animationEnter: { - duration: 300 - }, -}; - -const vchart = new VChart(spec, { dom: CONTAINER_ID }); -vchart.renderSync(); -``` - -## 相关教程 - -[VChart动画教程](/vchart/guide/tutorial_docs/Animation/Animation_Types) - diff --git a/docs/assets/examples/zh/storytelling/disappear-animation-wipe.md b/docs/assets/examples/zh/storytelling/disappear-animation-wipe.md new file mode 100644 index 0000000000..28e18e5c1d --- /dev/null +++ b/docs/assets/examples/zh/storytelling/disappear-animation-wipe.md @@ -0,0 +1,117 @@ +--- +category: examples +group: storytelling +title: Disappear Animation Wipe +keywords: animation,morphing,bar,pie,barChart,pieChart,comparison +order: 42-0 +cover: /vchart/preview/disappear-animation-wipe-2.0.11.gif +option: lineChart#animationAppear +--- + +# 退场动画擦除效果 + +在自定义动画效果中可以通过 `afterStageRender` 方法实现擦除动画效果。 + +## 关键配置 + +- callBack: 退场动画的自定义回调函数/动画实现类,用于执行用户自定义的退场效果。 + +### 代码演示 + +```javascript livedemo +// import { vglobal } from '@visactor/vchart'; +const vglobal = VCHART_MODULE.vglobal; + +function wipeAnimate(canvas, ratio) { + // 创建临时画布 + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; + } + + // 将原画布内容绘制到临时画布 + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvas, 0, 0); + + // 获取临时画布的图像数据 + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const data = imageData.data; + + // 根据ratio计算擦除位置(从左到右) + const wipePosition = Math.floor(canvas.width * ratio); + + // 设置渐变区域宽度,可根据需要调整 + const gradientWidth = Math.min(canvas.width * 0.3, 50); + + // 遍历每个像素点 + for (let y = 0; y < canvas.height; y++) { + for (let x = 0; x < canvas.width; x++) { + // 计算当前像素在数据数组中的索引 + const index = (y * canvas.width + x) * 4; + + // 计算当前像素的原始透明度 + const originalAlpha = data[index + 3]; + + // 计算当前像素与擦除位置的距离 + const distance = x - wipePosition; + + // 根据距离计算透明度 + let newAlpha; + if (distance <= 0) { + // 擦除位置左侧:完全透明 + newAlpha = 0; + } else if (distance <= gradientWidth) { + // 渐变区域内:透明度从0到原始透明度渐变 + const gradientRatio = distance / gradientWidth; + newAlpha = Math.floor(originalAlpha * gradientRatio); + } else { + // 擦除位置右侧:保持原始透明度 + newAlpha = originalAlpha; + } + + // 设置新的透明度 + data[index + 3] = newAlpha; + } + } + + // 将处理后的图像数据绘制回临时画布 + ctx.putImageData(imageData, 0, 0); + + return c; +} + +const spec = { + type: 'bar', + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + xField: 'month', + yField: 'sales', + animationDisappear: { + callBack: (stage, canvas, ratio) => wipeAnimate(canvas, ratio), + easing: 'linear', + duration: 2000 + } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderAsync(); + +setInterval(() => { + vchart.runDisappearAnimation(); +}, 3000); +``` diff --git a/docs/assets/examples/zh/storytelling/disappear-animation.md b/docs/assets/examples/zh/storytelling/disappear-animation.md new file mode 100644 index 0000000000..f649c3c987 --- /dev/null +++ b/docs/assets/examples/zh/storytelling/disappear-animation.md @@ -0,0 +1,101 @@ +--- +category: examples +group: storytelling +title: Disappear Animation +keywords: animation,morphing,bar,pie,barChart,pieChart,comparison +order: 42-0 +cover: /vchart/preview/disappear-animation-2.0.11.gif +option: lineChart#animationAppear +--- + +# 退场动画 + +用户可以手动调用 `vchart.runDisappearAnimation()` 方法执行退场动画。 +退场动画与入场、更新等其他动画配置的差异之处在于退场动画额外提供了 `callBack` 参数用于执行用户的自定义退场效果。 +用户可以在 `callBack` 参数中配置动画的自定义插值回调函数,或者提供一个继承自 `AStageAnimation` 的动画实现类。 + +## 关键配置 + +- callBack: 退场动画的自定义回调函数/动画实现类,用于执行用户自定义的退场效果。 + +### 代码演示 + +```javascript livedemo +// import { vglobal } from '@visactor/vchart'; +// import { AStageAnimate } from '@visactor/vrender-animate'; +const vglobal = VCHART_MODULE.vglobal; +const AStageAnimate = VRender.AStageAnimate; + +// 自定义退场动画实现类 +class TestStageAnimate extends AStageAnimate { + onUpdate(end, ratio, out) { + super.onUpdate(end, ratio, out); + this.ratio = ratio; + } + + // 更新画布渲染内容 + afterStageRender(stage, canvas) { + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; + } + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvas, 0, 0); + ctx.fillStyle = `rgba(255, 255, 255, ${this.ratio.toFixed(2)})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + return c; + } +} + +const spec = { + type: 'bar', + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + xField: 'month', + yField: 'sales', + animationDisappear: { + // 自定义动画执行回调函数 + callBack: (stage, canvas, ratio) => { + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; + } + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvas, 0, 0); + ctx.fillStyle = `rgba(255, 255, 255, ${ratio.toFixed(2)})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + return c; + }, + // 自定义动画执行类使用方式 + // callBack: TestStageAnimate, + duration: 1000 + } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderAsync(); + +setInterval(() => { + vchart.runDisappearAnimation(); +}, 2000); +``` diff --git a/docs/assets/option/en/animate/animation.md b/docs/assets/option/en/animate/animation.md index 2357a65195..5d108dab6c 100644 --- a/docs/assets/option/en/animate/animation.md +++ b/docs/assets/option/en/animate/animation.md @@ -135,36 +135,6 @@ ${markName} mark animation config. {{ /for }} -##${prefix} type='IStageAnimateSpec'(object) -Stage effect animation configuration. Provides various cool exit animation effects, including particle disintegration, distortion, blur effects, glitch art, pixelation, color effects, and dissolve effects. - -###${prefix} stage(object) -Stage animation configuration. - -####${prefix} type(string) -Animation effect type. Available values: - -- `'particle'`: Particle disintegration effect -- `'distortion'`: Distortion effect -- `'gaussianBlur'`: Gaussian blur effect -- `'glitch'`: Glitch art effect -- `'pixelation'`: Pixelation effect -- `'grayscale'`: Color effect -- `'dissolve'`: Dissolve effect - -####${prefix} duration(number) = 2000 -Animation duration in milliseconds. - -####${prefix} easing(string) = 'linear' -Animation easing effect. Supported values: 'linear', 'quadIn', 'quadOut', 'quadInOut', 'cubicIn', 'cubicOut', 'cubicInOut', 'quartIn', 'quartOut', 'quartInOut', 'quintIn', 'quintOut', 'quintInOut', 'backIn', 'backOut', 'backInOut', 'circIn', 'circOut', 'circInOut', 'bounceOut', 'bounceIn', 'bounceInOut', 'elasticIn', 'elasticOut', 'elasticInOut'. - -####${prefix} options(object) -Specific effect configuration parameters, which vary based on different `type` values. - -{{ use: animate-stage-options( prefix = '####' + ${prefix} ) }} - -{{ /for }} - #${prefix} animationState(boolean|object) Animation effect when the graphic state changes. Supported after version `1.12.0`. diff --git a/docs/assets/option/en/animate/stage-options.md b/docs/assets/option/en/animate/stage-options.md deleted file mode 100644 index 54d7b3a5d8..0000000000 --- a/docs/assets/option/en/animate/stage-options.md +++ /dev/null @@ -1,250 +0,0 @@ -{{ target: animate-stage-options }} - - - -#${prefix} type = 'particle' - -Particle Disintegration Effect -Configuration parameters when `type` is `'particle'`: - -##${prefix} effectType(string) = 'gravity' -Particle motion mode. Available values: - -- `'explode'`: Explosion effect - particles scatter outward from center -- `'vortex'`: Vortex effect - particles expand outward in a spiral pattern -- `'gravity'`: Gravity effect - particles disintegrate and fall - -##${prefix} count(number) = 4000 -Number of particles. Value range: 1000-8000. - -##${prefix} size(number) = 20 -Particle size. Value range: 2-15. - -##${prefix} strength(number) = 1.0 -Force field strength. Value range: 0.1-3.0. - -##${prefix} useWebGL(boolean) = true -Whether to use WebGL implementation. - -**Usage Example:** - -```typescript -animationDisappear: { - stage: { - type: 'particle', - duration: 3000, - easing: 'linear', - options: { - effectType: 'vortex', - count: 6000, - size: 8, - strength: 2.0, - useWebGL: true - } - } -} -``` - -#${prefix} type = 'distortion' - -Distortion Effect -Configuration parameters when `type` is `'distortion'`: - -##${prefix} distortionType(string) = 'wave' -Distortion effect type. Available values: - -- `'wave'`: Wave distortion - sinusoidal wave distortion producing wave effects -- `'ripple'`: Ripple distortion - circular ripples expanding from center point -- `'swirl'`: Swirl distortion - rotational distortion around center point - -##${prefix} strength(number) = 0.3 -Distortion strength. Value range: 0.0-2.0. - -##${prefix} useWebGL(boolean) = true -Whether to use WebGL implementation. - -**Usage Example:** - -```typescript -animationDisappear: { - stage: { - type: 'distortion', - duration: 3000, - easing: 'linear', - options: { - distortionType: 'wave', - strength: 0.8, - useWebGL: true - } - } -} -``` - -#${prefix} type = 'gaussianBlur' - -Gaussian Blur Effect -Configuration parameters when `type` is `'gaussianBlur'`: - -##${prefix} blurRadius(number) = 8 -Blur intensity. Value range: 1-20. - -##${prefix} useOptimizedBlur(boolean) = true -Whether to use optimized version. - -- `true`: High-performance mode - uses CSS filters with GPU acceleration -- `false`: High-quality mode - uses pixel-level downsampling algorithm - -**Usage Example:** - -```typescript -animationDisappear: { - stage: { - type: 'gaussianBlur', - duration: 1000, - easing: 'linear', - options: { - blurRadius: 10, - useOptimizedBlur: true - } - } -} -``` - -#${prefix} type = 'glitch' - -Glitch Art Effect -Configuration parameters when `type` is `'glitch'`: - -##${prefix} effectType(string) = 'rgb-shift' -Glitch effect type. Available values: - -- `'rgb-shift'`: RGB channel shift - red, green, blue color channel separation and offset -- `'digital-distortion'`: Digital distortion - horizontal slice offset + random pixel noise -- `'scan-lines'`: Scan line glitch - horizontal scan lines and bright line effects -- `'data-corruption'`: Data corruption - vertical stripes + block corruption - -##${prefix} intensity(number) = 0.5 -Glitch intensity. Value range: 0.0-1.0. - -**Usage Example:** - -```typescript -animationDisappear: { - stage: { - type: 'glitch', - duration: 1000, - easing: 'linear', - options: { - effectType: 'rgb-shift', - intensity: 0.5 - } - } -} -``` - -#${prefix} type = 'pixelation' - -Pixelation Effect -Configuration parameters when `type` is `'pixelation'`: - -##${prefix} maxPixelSize(number) = 20 -Maximum pixelation intensity. Value range: 1-50. - -##${prefix} method(string) = 'out' -Pixelation direction. Available values: - -- `'out'`: Exit effect - pixelation intensity gradually increases from 1 to maximum -- `'in'`: Enter effect - pixelation intensity gradually decreases from maximum to 1 - -**Usage Example:** - -```typescript -animationDisappear: { - stage: { - type: 'pixelation', - duration: 2000, - easing: 'linear', - options: { - maxPixelSize: 25, - method: 'out' - } - } -} -``` - -#${prefix} type = 'grayscale' - -Color Effect -Configuration parameters when `type` is `'grayscale'`: - -##${prefix} effectType(string) = 'grayscale' -Color effect type. Available values: - -- `'grayscale'`: Grayscale effect - converts to black and white using standard luminance formula -- `'sepia'`: Sepia effect - nostalgic style sepia filter - -##${prefix} strength(number) = 1.0 -Effect strength. Value range: 0.0-1.0. - -##${prefix} useWebGL(boolean) = true -Whether to use WebGL implementation. - -**Usage Example:** - -```typescript -animationDisappear: { - stage: { - type: 'grayscale', - duration: 2000, - easing: 'linear', - options: { - effectType: 'grayscale', - strength: 1.0, - useWebGL: true - } - } -} -``` - -#${prefix} type = 'dissolve' - -Dissolve Effect -Configuration parameters when `type` is `'dissolve'`: - -##${prefix} dissolveType(string) = 'outward' -Dissolve direction. Available values: - -- `'outward'`: Dissolve from center outward -- `'inward'`: Dissolve from outside to center -- `'radial'`: Radial dissolve -- `'leftToRight'`: Dissolve from left to right -- `'rightToLeft'`: Dissolve from right to left -- `'topToBottom'`: Dissolve from top to bottom -- `'bottomToTop'`: Dissolve from bottom to top - -##${prefix} noiseScale(number) = 8 -Noise scale. Value range: 1-20. - -##${prefix} fadeEdge(boolean) = true -Whether to use edge fading. - -##${prefix} strength(number) = 1.0 -Dissolve strength. Value range: 0.0-2.0. - -**Usage Example:** - -```typescript -animationDisappear: { - stage: { - type: 'dissolve', - duration: 2000, - easing: 'linear', - options: { - dissolveType: 'radial', - noiseScale: 10, - fadeEdge: true, - strength: 1.2 - } - } -} -``` diff --git a/docs/assets/option/zh/animate/animation.md b/docs/assets/option/zh/animate/animation.md index 7c4cf496d5..95e537ec7b 100644 --- a/docs/assets/option/zh/animate/animation.md +++ b/docs/assets/option/zh/animate/animation.md @@ -135,36 +135,6 @@ ${markName} 图元的动画配置。 {{ /for }} -##${prefix} type='IStageAnimateSpec'(object) -舞台特效动画配置。提供多种炫酷的退场动画效果,包括粒子分解、扭曲变形、模糊效果、故障艺术、像素化、颜色效果和溶解效果等。 - -###${prefix} stage(object) -舞台动画配置。 - -####${prefix} type(string) -动画效果类型。可选值: - -- `'particle'`: 粒子分解效果 -- `'distortion'`: 扭曲变形效果 -- `'gaussianBlur'`: 高斯模糊效果 -- `'glitch'`: 故障艺术效果 -- `'pixelation'`: 像素化效果 -- `'grayscale'`: 颜色效果 -- `'dissolve'`: 溶解效果 - -####${prefix} duration(number) = 2000 -动画持续时间,单位毫秒。 - -####${prefix} easing(string) = 'linear' -动画缓动效果。支持的值有:'linear', 'quadIn', 'quadOut', 'quadInOut', 'cubicIn', 'cubicOut', 'cubicInOut', 'quartIn', 'quartOut', 'quartInOut', 'quintIn', 'quintOut', 'quintInOut', 'backIn', 'backOut', 'backInOut', 'circIn', 'circOut', 'circInOut', 'bounceOut', 'bounceIn', 'bounceInOut', 'elasticIn', 'elasticOut', 'elasticInOut'。 - -####${prefix} options(object) -具体效果配置参数,根据不同的 `type` 有不同的配置项。 - -{{ use: animate-stage-options( prefix = '####' + ${prefix} ) }} - -{{ /for }} - #${prefix} animationState(boolean|object) 图元状态切换时的动画效果。自 `1.12.0` 版本后支持。 diff --git a/docs/assets/option/zh/animate/stage-options.md b/docs/assets/option/zh/animate/stage-options.md deleted file mode 100644 index 2f7fbf4de6..0000000000 --- a/docs/assets/option/zh/animate/stage-options.md +++ /dev/null @@ -1,246 +0,0 @@ -{{ target: animate-stage-options }} - - - -#${prefix} type = 'particle' - -粒子分解效果 -当 `type` 为 `'particle'` 时的配置参数: - -##${prefix} effectType(string) = 'gravity' -粒子运动模式。可选值: - -- `'explode'`: 爆炸效果 - 粒子从中心向外飞散 -- `'vortex'`: 漩涡效果 - 粒子以螺旋方式向外扩散 -- `'gravity'`: 重力效果 - 粒子瓦解下落 - -##${prefix} count(number) = 4000 -粒子数量。取值范围:1000-8000。 - -##${prefix} size(number) = 20 -粒子大小。取值范围:2-15。 - -##${prefix} strength(number) = 1.0 -力场强度。取值范围:0.1-3.0。 - -##${prefix} useWebGL(boolean) = true -是否使用 WebGL 实现。 - -**使用示例:** - -```typescript -animationDisappear: { - stage: { - type: 'particle', - duration: 3000, - easing: 'linear', - options: { - effectType: 'vortex', - count: 6000, - size: 8, - strength: 2.0, - useWebGL: true - } - } -} -``` - -#${prefix} type = 'distortion' - -扭曲变形效果 -当 `type` 为 `'distortion'` 时的配置参数: - -##${prefix} distortionType(string) = 'wave' -扭曲效果类型。可选值: - -- `'wave'`: 波浪扭曲 - 正弦波形扭曲,产生波浪效果 -- `'ripple'`: 涟漪扭曲 - 从中心点扩散的圆形波纹 -- `'swirl'`: 漩涡扭曲 - 围绕中心点的旋转扭曲 - -##${prefix} strength(number) = 0.3 -扭曲强度。取值范围:0.0-2.0。 - -##${prefix} useWebGL(boolean) = true -是否使用 WebGL 实现。 - -**使用示例:** - -```typescript -animationDisappear: { - stage: { - type: 'distortion', - duration: 3000, - easing: 'linear', - options: { - distortionType: 'wave', - strength: 0.8, - useWebGL: true - } - } -} -``` - -#${prefix} type = 'gaussianBlur' - -高斯模糊效果 -当 `type` 为 `'gaussianBlur'` 时的配置参数: - -##${prefix} blurRadius(number) = 8 -模糊强度。取值范围:1-20。 - -##${prefix} useOptimizedBlur(boolean) = true -是否使用优化版本。 - -- `true`: 高性能模式 - 使用 CSS 滤镜,GPU 加速 -- `false`: 高质量模式 - 使用像素级降采样算法 - -**使用示例:** - -```typescript -animationDisappear: { - stage: { - type: 'gaussianBlur', - duration: 1000, - easing: 'linear', - options: { - blurRadius: 10, - useOptimizedBlur: true - } - } -} -``` - -#${prefix} type = 'glitch' - -故障艺术效果 -当 `type` 为 `'glitch'` 时的配置参数: - -##${prefix} effectType(string) = 'rgb-shift' -故障效果类型。可选值: - -- `'rgb-shift'`: RGB 通道偏移 - 红绿蓝颜色通道分离偏移 -- `'digital-distortion'`: 数字扭曲 - 水平切片偏移 + 随机像素噪声 -- `'scan-lines'`: 扫描线故障 - 水平扫描线和亮线效果 -- `'data-corruption'`: 数据损坏 - 垂直条纹 + 块状损坏 - -##${prefix} intensity(number) = 0.5 -故障强度。取值范围:0.0-1.0。 - -**使用示例:** - -```typescript -animationDisappear: { - stage: { - type: 'glitch', - duration: 1000, - easing: 'linear', - options: { - effectType: 'rgb-shift', - intensity: 0.5 - } - } -} -``` - -#${prefix} type = 'pixelation' - -像素化效果 -当 `type` 为 `'pixelation'` 时的配置参数: - -##${prefix} maxPixelSize(number) = 20 -最大像素化强度。取值范围:1-50。 - -##${prefix} method(string) = 'out' -像素化方向。可选值: - -- `'out'`: 退场效果 - 像素化强度从 1 逐渐增加到最大值 -- `'in'`: 入场效果 - 像素化强度从最大值逐渐减小到 1 - -**使用示例:** - -```typescript -animationDisappear: { - stage: { - type: 'pixelation', - duration: 2000, - easing: 'linear', - options: { - maxPixelSize: 25, - method: 'out' - } - } -} -``` - -#${prefix} type = 'grayscale' - -颜色效果 -当 `type` 为 `'grayscale'` 时的配置参数: - -##${prefix} effectType(string) = 'grayscale' -颜色效果类型。可选值: - -- `'grayscale'`: 灰度效果 - 使用标准亮度公式转为黑白 -- `'sepia'`: 褐色调效果 - 怀旧风格的褐色滤镜 - -##${prefix} strength(number) = 1.0 -效果强度。取值范围:0.0-1.0。 - -##${prefix} useWebGL(boolean) = true -是否使用 WebGL 实现。 - -**使用示例:** - -```typescript -animationDisappear: { - stage: { - type: 'grayscale', - duration: 2000, - easing: 'linear', - options: { - effectType: 'grayscale', - strength: 1.0, - useWebGL: true - } - } -} -``` - -#${prefix} type = 'dissolve' - -溶解效果 -当 `type` 为 `'dissolve'` 时的配置参数: - -##${prefix} dissolveType(string) = 'outward' -溶解方向。可选值: - -- `'outward'`: 从中心向外溶解 -- `'inward'`: 从外向中心溶解 -- `'radial'`: 径向溶解 -- `'leftToRight'`: 从左到右溶解 -- `'rightToLeft'`: 从右到左溶解 -- `'topToBottom'`: 从上到下溶解 -- `'bottomToTop'`: 从下到上溶解 - -##${prefix} noiseScale(number) = 8 -噪声比例。取值范围:1-20。 - -##${prefix} fadeEdge(boolean) = true -是否边缘渐变。 - -**使用示例:** - -```typescript -animationDisappear: { - stage: { - type: 'dissolve', - duration: 2000, - easing: 'linear', - options: { - dissolveType: 'radial', - noiseScale: 10, - fadeEdge: true, - } - } -} -``` diff --git a/docs/public/vchart/preview/disappear-animation-2.0.11.gif b/docs/public/vchart/preview/disappear-animation-2.0.11.gif new file mode 100644 index 0000000000..2515a9ee59 Binary files /dev/null and b/docs/public/vchart/preview/disappear-animation-2.0.11.gif differ diff --git a/docs/public/vchart/preview/disappear-animation-gaussian-blur-2.0.11.gif b/docs/public/vchart/preview/disappear-animation-gaussian-blur-2.0.11.gif new file mode 100644 index 0000000000..4240c92c18 Binary files /dev/null and b/docs/public/vchart/preview/disappear-animation-gaussian-blur-2.0.11.gif differ diff --git a/docs/public/vchart/preview/disappear-animation-wipe-2.0.11.gif b/docs/public/vchart/preview/disappear-animation-wipe-2.0.11.gif new file mode 100644 index 0000000000..328307eaf2 Binary files /dev/null and b/docs/public/vchart/preview/disappear-animation-wipe-2.0.11.gif differ diff --git a/packages/vchart/__tests__/runtime/browser/test-page/area.ts b/packages/vchart/__tests__/runtime/browser/test-page/area.ts index 28a14ab2ab..bf70c30dba 100644 --- a/packages/vchart/__tests__/runtime/browser/test-page/area.ts +++ b/packages/vchart/__tests__/runtime/browser/test-page/area.ts @@ -1,5 +1,4 @@ import { isMobile } from 'react-device-detect'; -import type { IAreaChartSpec } from '../../../../src/index'; // eslint-disable-next-line no-duplicate-imports import { default as VChart, @@ -9,162 +8,104 @@ import { registerStateTransition, vglobal } from '../../../../src/index'; -import { AnimateExecutor, AStageAnimate } from '@visactor/vrender-animate'; registerAnimate(); registerCustomAnimate(); registerStateTransition(); -class TestStageAnimate extends AStageAnimate { - // 原版代码 - protected afterStageRender(stage: any, canvas: HTMLCanvasElement): HTMLCanvasElement | void | null | false { - const c = vglobal.createCanvas({ - width: canvas.width, - height: canvas.height, - dpr: vglobal.devicePixelRatio - }); - const ctx = c.getContext('2d'); - if (!ctx) { - return false; - } - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(canvas, 0, 0); - ctx.fillStyle = `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor( - Math.random() * 255 - )}, 0.2)`; - ctx.fillRect(0, 0, canvas.width, canvas.height); - return c; +function wipeAnimate(canvas, ratio) { + // 创建临时画布 + const c = vglobal.createCanvas({ + width: canvas.width, + height: canvas.height, + dpr: vglobal.devicePixelRatio + }); + const ctx = c.getContext('2d'); + if (!ctx) { + return false; } -} -AnimateExecutor.registerBuiltInAnimate('stageTest', TestStageAnimate); + // 将原画布内容绘制到临时画布 + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvas, 0, 0); + + // 获取临时画布的图像数据 + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const data = imageData.data; + + // 根据ratio计算擦除位置(从左到右) + const wipePosition = Math.floor(canvas.width * ratio); + + // 设置渐变区域宽度,可根据需要调整 + const gradientWidth = Math.min(canvas.width * 0.3, 50); + + // 遍历每个像素点 + for (let y = 0; y < canvas.height; y++) { + for (let x = 0; x < canvas.width; x++) { + // 计算当前像素在数据数组中的索引 + const index = (y * canvas.width + x) * 4; + + // 计算当前像素的原始透明度 + const originalAlpha = data[index + 3]; + + // 计算当前像素与擦除位置的距离 + const distance = x - wipePosition; + + // 根据距离计算透明度 + let newAlpha; + if (distance <= 0) { + // 擦除位置左侧:完全透明 + newAlpha = 0; + } else if (distance <= gradientWidth) { + // 渐变区域内:透明度从0到原始透明度渐变 + const gradientRatio = distance / gradientWidth; + newAlpha = Math.floor(originalAlpha * gradientRatio); + } else { + // 擦除位置右侧:保持原始透明度 + newAlpha = originalAlpha; + } -let dataArray = [ - { type: 'Nail polish', country: 'Africa', value: 4229 }, - { type: 'Nail polish', country: 'EU', value: 4376 }, - { type: 'Nail polish', country: 'China', value: 3054 }, - { type: 'Nail polish', country: 'USA', value: 12814 }, - { type: 'Eyebrow pencil', country: 'Africa', value: 3932 }, - { type: 'Eyebrow pencil', country: 'EU', value: 3987 }, - { type: 'Eyebrow pencil', country: 'China', value: 5067 }, - { type: 'Eyebrow pencil', country: 'USA', value: 13012 }, - { type: 'Rouge', country: 'Africa', value: 5221 }, - { type: 'Rouge', country: 'EU', value: 3574 }, - { type: 'Rouge', country: 'China', value: 7004 }, - { type: 'Rouge', country: 'USA', value: 11624 }, - { type: 'Lipstick', country: 'Africa', value: 9256 }, - { type: 'Lipstick', country: 'EU', value: 4376 }, - { type: 'Lipstick', country: 'China', value: 9054 }, - { type: 'Lipstick', country: 'USA', value: 8814 }, - { type: 'Eyeshadows', country: 'Africa', value: 3308 }, - { type: 'Eyeshadows', country: 'EU', value: 4572 }, - { type: 'Eyeshadows', country: 'China', value: 12043 }, - { type: 'Eyeshadows', country: 'USA', value: 12998 }, - { type: 'Eyeliner', country: 'Africa', value: 5432 }, - { type: 'Eyeliner', country: 'EU', value: 3417 }, - { type: 'Eyeliner', country: 'China', value: 15067 }, - { type: 'Eyeliner', country: 'USA', value: 12321 }, - { type: 'Foundation', country: 'Africa', value: 13701 }, - { type: 'Foundation', country: 'EU', value: 5231 }, - { type: 'Foundation', country: 'China', value: 10119 }, - { type: 'Foundation', country: 'USA', value: 10342 }, - { type: 'Lip gloss', country: 'Africa', value: 4008 }, - { type: 'Lip gloss', country: 'EU', value: 4572 }, - { type: 'Lip gloss', country: 'China', value: 12043 }, - { type: 'Lip gloss', country: 'USA', value: 22998 }, - { type: 'Mascara', country: 'Africa', value: 18712 }, - { type: 'Mascara', country: 'EU', value: 6134 }, - { type: 'Mascara', country: 'China', value: 10419 }, - { type: 'Mascara', country: 'USA', value: 11261 } -]; + // 设置新的透明度 + data[index + 3] = newAlpha; + } + } -const direction: string = 'vertical'; + // 将处理后的图像数据绘制回临时画布 + ctx.putImageData(imageData, 0, 0); -let spec = { + return c; +} + +const spec = { type: 'bar', - data: { - id: 'data0', - values: dataArray - }, - title: { - visible: true, - text: 'Stacked area chart of cosmetic products sales' - }, - direction, - useSequentialAnimation: true, - // stack: true, - xField: direction === 'horizontal' ? 'value' : 'type', - yField: direction === 'horizontal' ? 'type' : 'value', - seriesField: 'country', - legends: [{ visible: true, position: 'middle' as const, orient: 'bottom' as const }], - crosshair: { - followTooltip: true, - xField: { visible: true, label: { visible: true } }, - yField: { visible: true, label: { visible: true } } - }, - // animationAppear: { - // duration: 300 - // }, - animationAppear: { - stage: { - type: 'stageTest', - duration: 1000, - easing: 'linear' - } - }, - animationUpdate: { - duration: 300 - }, - animationEnter: { - duration: 300 - }, - animationExit: { - duration: 2000, - type: 'fadeOut' - }, - animationNormal: { - point: [ - { - loop: true, - startTime: 100, - oneByOne: 100, - priority: 1, - timeSlices: [ - { - delay: 1000, - effects: { - channel: { - fillOpacity: { to: 0.5 } - }, - easing: 'linear' - }, - duration: 500 - }, - { - effects: { - channel: { - fillOpacity: { to: 1 } - }, - easing: 'linear' - }, - duration: 500 - } - ] - } - ] - }, - point: { - state: { - hover: { - fill: 'red' - } - }, - style: { - size: 80 + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] } + ], + xField: 'month', + yField: 'sales', + animationDisappear: { + callBack: (stage, canvas, ratio) => wipeAnimate(canvas, ratio), + easing: 'linear', + duration: 2000 } }; const run = () => { + const container = document.getElementById('chart'); + if (container) { + container.style.width = '640px'; + container.style.height = '480px'; + container.style.border = '1px solid #eee'; + } + registerMediaQuery(); // VChart.ThemeManager.setCurrentTheme('dark'); const cs = new VChart(spec, { @@ -182,194 +123,17 @@ const run = () => { }); const button = document.createElement('button'); - button.innerHTML = 'click'; + button.innerHTML = '退场动画'; button.addEventListener('click', () => { - dataArray = dataArray.map(d => ({ ...d, value: 100000 * Math.random() })); - cs.updateData('data0', dataArray); + cs.runDisappearAnimation(); }); - document.body.appendChild(button); + // document.body.appendChild(button); - const button2 = document.createElement('button'); - button2.innerHTML = 'add&remove'; - button2.addEventListener('click', () => { - const name = Math.random().toString(); - dataArray = dataArray.map(d => ({ - ...d, - value: 100000 * Math.random(), - country: ['EU', 'China', 'USA'].includes(d.country) ? d.country : name - })); - cs.updateData('data0', dataArray); - }); - document.body.appendChild(button2); - - function addData() { - const name = Math.random().toString(); - const newData = dataArray - .filter(d => d.country === 'EU') - .map(d => ({ - ...d, - value: 100000 * Math.random(), - country: name - })); - - dataArray = [...dataArray, ...newData]; - } - - function removeData() { - dataArray = dataArray.filter(d => ['EU', 'China', 'USA'].includes(d.country)); - } - - const button3 = document.createElement('button'); - button3.innerHTML = 'stack<->group'; - button3.addEventListener('click', () => { - const nextSpec: any = { ...spec }; - const fieldKey = direction === 'horizontal' ? 'yField' : 'xField'; - if (typeof nextSpec[fieldKey] === 'string') { - (nextSpec as any)[fieldKey] = ['type', 'country']; - } else { - (nextSpec as any)[fieldKey] = 'type'; - } - spec = nextSpec; - cs.updateSpec(spec as any); - }); - document.body.appendChild(button3); - - const button4 = document.createElement('button'); - button4.innerHTML = 'stack<->group add'; - button4.addEventListener('click', () => { - const nextSpec: any = { ...spec }; - const fieldKey = direction === 'horizontal' ? 'yField' : 'xField'; - if (typeof nextSpec[fieldKey] === 'string') { - (nextSpec as any)[fieldKey] = ['type', 'country']; - } else { - (nextSpec as any)[fieldKey] = 'type'; - } - addData(); - nextSpec.data = { - id: 'data0', - values: dataArray - }; - spec = nextSpec; - cs.updateSpec(spec as any); - }); - document.body.appendChild(button4); - - const button5 = document.createElement('button'); - button5.innerHTML = 'stack<->group remove'; - button5.addEventListener('click', () => { - const nextSpec: any = { ...spec }; - const fieldKey = direction === 'horizontal' ? 'yField' : 'xField'; - if (typeof nextSpec[fieldKey] === 'string') { - (nextSpec as any)[fieldKey] = ['type', 'country']; - } else { - (nextSpec as any)[fieldKey] = 'type'; - } - removeData(); - nextSpec.data = { - id: 'data0', - values: dataArray - }; - spec = nextSpec; - cs.updateSpec(spec as any); - }); - document.body.appendChild(button5); - - const button6 = document.createElement('button'); - button6.innerHTML = 'direction'; - button6.addEventListener('click', () => { - const nextSpec: any = { ...spec }; - nextSpec.direction = nextSpec.direction === 'horizontal' ? 'vertical' : 'horizontal'; - [nextSpec.xField, nextSpec.yField] = [nextSpec.yField, nextSpec.xField]; - spec = nextSpec; - cs.updateSpec(spec as any); - }); - document.body.appendChild(button6); - - // 添加退场动画按钮 - const button7 = document.createElement('button'); - button7.innerHTML = '触发退场动画'; - button7.addEventListener('click', () => { - console.log('触发退场动画...'); - - // 方法1: 通过移除数据来触发退场动画 - // 先保存原始数据 - const originalData = [...dataArray]; - - // 清空数据,这会触发所有元素的退场动画 - cs.updateData('data0', []); - - // 3秒后恢复数据,重新显示图表 - setTimeout(() => { - console.log('恢复数据,重新显示图表...'); - cs.updateData('data0', originalData); - }, 3000); - }); - document.body.appendChild(button7); - - // 添加第三种退场动画按钮 - 通过更新spec来触发 - const button9 = document.createElement('button'); - button9.innerHTML = 'Spec退场动画'; - button9.addEventListener('click', () => { - console.log('通过更新spec触发退场动画...'); - - // 保存原始spec - const originalSpec = { ...spec }; - - // 创建一个空的spec来触发退场动画 - const emptySpec = { - ...spec, - data: { - id: 'data0', - values: [] // 空数据 - } - }; - - // 更新spec,这会触发退场动画 - cs.updateSpec(emptySpec as any); - - // 3秒后恢复原始spec - setTimeout(() => { - console.log('恢复原始spec...'); - cs.updateSpec(originalSpec as any); - }, 3000); - }); - document.body.appendChild(button9); - - // 添加自定义退场动画按钮 - const button10 = document.createElement('button'); - button10.innerHTML = '自定义退场动画'; - button10.addEventListener('click', () => { - console.log('触发自定义退场动画...'); - - // 保存原始spec - const originalSpec = { ...spec }; - - // 创建带有自定义退场动画的spec - const customExitSpec = { - ...spec, - animationExit: { - duration: 1000, // 1秒退场动画 - type: 'fadeOut', // 淡出效果 - easing: 'easeInOut' // 缓动函数 - }, - data: { - id: 'data0', - values: [] // 空数据触发退场 - } - }; - - // 更新spec,触发自定义退场动画 - cs.updateSpec(customExitSpec as any); - - // 2秒后恢复原始spec - setTimeout(() => { - console.log('恢复原始spec...'); - cs.updateSpec(originalSpec as any); - }, 2000); - }); - document.body.appendChild(button10); + // setInterval(() => { + // cs.runDisappearAnimation(); + // }, 2000); - window['vchart'] = cs; + (window as any)['vchart'] = cs; console.log(cs); }; run(); diff --git a/packages/vchart/src/animation/callback-disappear.ts b/packages/vchart/src/animation/callback-disappear.ts index 94b61eb26d..22205537c7 100644 --- a/packages/vchart/src/animation/callback-disappear.ts +++ b/packages/vchart/src/animation/callback-disappear.ts @@ -29,6 +29,6 @@ export class CallbackDisappearAnimate extends AStageAnimate { } protected afterStageRender(stage: any, canvas: HTMLCanvasElement): void { - this.params?.callBack?.(stage, canvas, this.currentAnimationRatio, this.animationTime); + return this.params?.callBack?.(stage, canvas, this.currentAnimationRatio, this.animationTime); } } diff --git a/packages/vchart/src/animation/interface.ts b/packages/vchart/src/animation/interface.ts index cf900fdb59..ef6eeb08f1 100644 --- a/packages/vchart/src/animation/interface.ts +++ b/packages/vchart/src/animation/interface.ts @@ -162,7 +162,7 @@ export type IAnimationConfig = IAnimationTimeline | IAnimationTypeConfig; export type IStageAnimationCallback = (stage: IStage, canvas: HTMLCanvasElement, ratio: number, time: number) => void; export interface MarkAnimationSpec { - disappear?: IStateAnimationConfig & { + disappear?: IAnimationConfig & { callBack?: IStageAnimationCallback | AStageAnimate; }; appear?: IAnimationConfig | IAnimationConfig[];