平时当我们遇到要将dom元素转canvas进行截图时,大家应该都知道有个html2canvas的库可以很好的解决这个问题。那么如果我们想要将dom元素转canvas时,将dom的动画效果也能一块转过去,该怎么做呢?本文给大家提供一个参考方案。
效果演示
方案概述
- @tweenjs/tween.js: 这是一个js缓动动画库,通过js实现动画可以兼容dom动画效果和canvas中元素动画效果。
- pixijs: 2D绘图引擎,用来
关键代码讲解
<template>
<div class="box">
<a-space direction="vertical">
<a-row>
<a-space>
<a-button @click="() => transformToCanvas()">转canvas</a-button>
<a-select
ref="select"
v-model:value="animationValue"
style="width: 120px"
@change="handleChange"
>
<a-select-option
v-for="(i, index) in animateFuncsList"
:value="index"
>{{ i.name }}</a-select-option
>
</a-select>
</a-space>
</a-row>
<a-row>
<div class="wrap">
<img class="animate-img" src="@/assets/imgs/duck.jpeg" alt="" />
</div>
</a-row>
<a-row>
<div class="canvas-wrap"></div>
</a-row>
</a-space>
</div>
</template>
<script lang="ts" setup>
import TWEEN from "@tweenjs/tween.js";
import * as PIXI from "pixi.js";
import { onMounted, ref } from "vue";
import { animateFuncsList } from "./anmiteList";
const animationValue = ref(0); // 动画方式
let app: PIXI.Application;
let imgDom: HTMLElement;
let sprite: PIXI.Sprite;
onMounted(() => {
let wrapDom = document.querySelector(".wrap");
imgDom = document.querySelector(".animate-img") as HTMLElement;
// 初始化画布
app = new PIXI.Application({
width: wrapDom?.clientWidth,
height: wrapDom?.clientHeight,
backgroundAlpha: 0,
});
document.querySelector(".canvas-wrap")?.appendChild(app.view);
// Setup the animation loop.
function animate(time: number | undefined) {
requestAnimationFrame(animate);
TWEEN.update(time);
}
requestAnimationFrame(animate);
showAnimation(imgDom, animationValue.value);
});
function transformToCanvas(onComplete?: Function) {
app.stage.removeChildren();
// create a new Sprite from an image path
sprite = PIXI.Sprite.from("/assets/imgs/duck.jpeg");
sprite.width = imgDom.clientWidth;
sprite.height = imgDom.clientHeight;
sprite.alpha = 0;
app.stage.addChild(sprite);
showAnimation(sprite, animationValue.value, onComplete);
}
function handleChange(value: number) {
showAnimation(imgDom, value);
}
function showAnimation(
target: HTMLElement | PIXI.Sprite,
index: number,
onComplete?: Function
) {
animateFuncsList[index].call(target, onComplete);
}
</script>
<style scoped lang="scss">
.box {
padding-top: 60px;
.wrap {
width: 300px;
height: 300px;
background-color: black;
}
&-canvas {
width: 300px;
height: 300px;
}
}
</style>
anmiteList.ts: 中封装动画函数
import * as PIXI from "pixi.js";
import TWEEN from "@tweenjs/tween.js";
function fadeInRight(target: HTMLElement | PIXI.Sprite, onComplete?: Function) {
const style = { x: 300, opacity: 0 };
const tween = new TWEEN.Tween(style) // Create a new tween that modifies 'coords'.
.to({ x: 0, opacity: 1 }, 1000)
.easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onUpdate(() => {
if (target instanceof Element) {
target.style.setProperty("transform", `translate(${style.x}px)`);
target.style.setProperty("opacity", `${style.opacity}`);
} else if (target instanceof PIXI.Sprite) {
target.x = style.x;
target.alpha = style.opacity;
}
})
.onComplete(() => {
onComplete && onComplete();
})
.start(); // Start the tween immediately.
}
function bounceIn(target: HTMLElement | PIXI.Sprite, onComplete?: Function) {
const style = { scale: 0, opacity: 0 };
let scale = 1;
if (target instanceof PIXI.Sprite) {
target.anchor.set(0.5);
target.x = target.x + target.width / 2;
target.y = target.y + target.height / 2;
// scale = target.scale.x;
// console.log(111, scale)
}
const tween = new TWEEN.Tween(style) // Create a new tween that modifies 'coords'.
.to({ scale, opacity: 1 }, 1000)
.easing(TWEEN.Easing.Bounce.Out) // Use an easing function to make the animation smooth.
.onUpdate(() => {
if (target instanceof Element) {
target.style.setProperty("transform", `scale(${style.scale})`);
target.style.setProperty("opacity", `${style.opacity}`);
} else if (target instanceof PIXI.Sprite) {
target.scale.set(style.scale);
target.alpha = style.opacity;
}
})
.onComplete(() => {
onComplete && onComplete();
})
.start(); // Start the tween immediately.
}
function fadeInLeft(target: HTMLElement | PIXI.Sprite, onComplete?: Function) {
const style = { x: -300, opacity: 0 };
const tween = new TWEEN.Tween(style) // Create a new tween that modifies 'coords'.
.to({ x: 0, opacity: 1 }, 1000)
.easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
.onUpdate(() => {
if (target instanceof Element) {
target.style.setProperty("transform", `translate(${style.x}px)`);
target.style.setProperty("opacity", `${style.opacity}`);
} else if (target instanceof PIXI.Sprite) {
target.x = style.x;
target.alpha = style.opacity;
}
})
.onComplete(() => {
onComplete && onComplete();
})
.start(); // Start the tween immediately.
}
export let animateFuncsList = [
{ name: "右滑入场", call: fadeInRight },
{ name: "左滑入场", call: fadeInLeft },
{
name: "弹入",
call: bounceIn,
},
];