数据可视化大屏 项目开发中,会添加动画来点缀页面,有些可以通过css来实现动画,有些图片序列帧动画,序列帧动画可以通过css+雪碧图的方式来实现,也可以通过js的方式来实现,今天是使用canvas+js来实现。
准备序列帧图片
我们首先需要准备序列帧图片,这里我们准备了75张序列帧图片,我们可以通过fileLength来告知组件帧动画图片的数量,使用IntervalTime来控制帧动画的执行间隔。
组件使用
<template>
<div class="pageTop wow fadeInDown">
<sequence fileLength="74" IntervalTime="50"></sequence>
<div class="left"></div>
<div class="title">
<span>{{ title }}</span>
</div>
<div class="right">
</div>
</div>
</template>
<script>
import sequence from "./sequence/index.vue";
export default {
name: "pageTop",
components: {sequence},
data() {
return {
};
},
props: {
title: {
type: String,
default() {
return '';
}
}
}
}
</script>
vue2不用import写法
图片要放在public/bg目录下
<template>
<canvas ref="animationCanvas" class="animation_canvas" id="animation_canvas"></canvas>
</template>
<script>
export default {
props: {
// 文件数量
fileLength: {
type: Number,
default: 74
},
// 动画间隔
IntervalTime: {
type: Number,
default: 80
}
},
data() {
return {
animationCanvas: null
};
},
methods: {
loadImages2(sources) {
for (let i = 1; i <= this.fileLength; i++) {
const image = `./bg/${i}.png`; // 假设图片存放在 public/bg 目录下
sources.push(image);
}
},
loadImages(sources, callback) {
let loadedImages = 0;
const numImages = sources.length;
const images = [];
for (let i = 0; i < numImages; i++) {
images[i] = new Image();
images[i].onload = () => {
if (++loadedImages >= numImages) {
callback(images);
}
};
images[i].src = sources[i];
}
},
playImages(images, ctx, width, height, intervalTime) {
let imageNow = 0;
setInterval(() => {
ctx.clearRect(0, 0, width, height);
ctx.drawImage(images[imageNow], 0, 0, width, height);
imageNow = (imageNow + 1) % images.length;
}, intervalTime);
}
},
mounted() {
this.$nextTick(() => {
const canvas = this.$refs.animationCanvas;
if (canvas) {
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error('Failed to get canvas context');
return;
}
const sources = [];
// 先加载第一张图片并展示
const firstImage = `./bg/0.png`; // 假设图片存放在 public/bg 目录下
sources.push(firstImage);
const width = canvas.offsetWidth;
const height = canvas.offsetHeight;
canvas.width = width;
canvas.height = height;
const firstImgElement = new Image();
firstImgElement.src = sources[0];
firstImgElement.onload = () => {
ctx.drawImage(firstImgElement, 0, 0, width, height);
};
// 加载剩余的图片
this.loadImages2(sources);
// 执行图片预加载,加载完成后执行动画
this.loadImages(sources, (images) => {
this.playImages(images, ctx, width, height, this.IntervalTime);
});
}
});
}
};
</script>
<style scoped>
.animation_canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
</style>
vue2 import写法
<template>
<canvas ref="animationCanvas" class="animation_canvas" id="animation_canvas"></canvas>
</template>
<script>
export default {
props: {
// 文件数量
fileLength: {
type: Number,
default: 74
},
// 动画间隔
IntervalTime: {
type: Number,
default: 80
}
},
data() {
return {
animationCanvas: null
};
},
methods: {
async loadImages2(sources) {
for (let i = 1; i <= this.fileLength; i++) {
const image = await import(`./bg/${i}.png`);
sources.push(image.default);
}
},
loadImages(sources, callback) {
let loadedImages = 0;
const numImages = sources.length;
const images = [];
for (let i = 0; i < numImages; i++) {
images[i] = new Image();
images[i].onload = () => {
if (++loadedImages >= numImages) {
callback(images);
}
};
images[i].src = sources[i];
}
},
playImages(images, ctx, width, height, intervalTime) {
let imageNow = 0;
setInterval(() => {
ctx.clearRect(0, 0, width, height);
ctx.drawImage(images[imageNow], 0, 0, width, height);
imageNow = (imageNow + 1) % images.length;
}, intervalTime);
}
},
async mounted() {
this.$nextTick(async () => {
const canvas = this.$refs.animationCanvas;
if (canvas) {
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error('Failed to get canvas context');
return;
}
const sources = [];
// 先加载第一张图片并展示
const firstImage = await import(`./bg/0.png`);
sources.push(firstImage.default);
const width = canvas.offsetWidth;
const height = canvas.offsetHeight;
canvas.width = width;
canvas.height = height;
const firstImgElement = new Image();
firstImgElement.src = sources[0];
firstImgElement.onload = () => {
ctx.drawImage(firstImgElement, 0, 0, width, height);
};
// 加载剩余的图片
await this.loadImages2(sources);
// 执行图片预加载,加载完成后执行动画
this.loadImages(sources, (images) => {
this.playImages(images, ctx, width, height, this.IntervalTime);
});
}
});
}
};
</script>
<style scoped>
.animation_canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
</style>
vue3写法
<template>
<canvas ref="animationCanvas" class="animation_canvas" id="animation_canvas"></canvas>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue';
// Props
const props = defineProps({
// 文件数量
fileLength: {
type: Number,
default: 74
},
// 动画间隔
IntervalTime: {
type: Number,
default: 80
}
});
// Refs
const animationCanvas = ref(null);
// Methods
const loadImages2 = async (sources) => {
for (let i = 1; i <= props.fileLength; i++) {
const image = await import(`./bg/${i}.png`);
sources.push(image.default);
}
};
const loadImages = (sources, callback) => {
let loadedImages = 0;
const numImages = sources.length;
const images = [];
for (let i = 0; i < numImages; i++) {
images[i] = new Image();
images[i].onload = () => {
if (++loadedImages >= numImages) {
callback(images);
}
};
images[i].src = sources[i];
}
};
const playImages = (images, ctx, width, height, intervalTime) => {
let imageNow = 0;
setInterval(() => {
ctx.clearRect(0, 0, width, height);
ctx.drawImage(images[imageNow], 0, 0, width, height);
imageNow = (imageNow + 1) % images.length;
}, intervalTime);
};
// Lifecycle hook
onMounted(async () => {
await nextTick();
const canvas = animationCanvas.value;
if (canvas) {
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error('Failed to get canvas context');
return;
}
const sources = [];
// 先加载第一张图片并展示
const firstImage = await import(`./bg/0.png`);
sources.push(firstImage.default);
const width = canvas.offsetWidth;
const height = canvas.offsetHeight;
canvas.width = width;
canvas.height = height;
const firstImgElement = new Image();
firstImgElement.src = sources[0];
firstImgElement.onload = () => {
ctx.drawImage(firstImgElement, 0, 0, width, height);
};
// 加载剩余的图片
await loadImages2(sources);
// 执行图片预加载,加载完成后执行动画
loadImages(sources, (images) => {
playImages(images, ctx, width, height, props.IntervalTime);
});
}
});
</script>
<style scoped>
.animation_canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
</style>