首先先看效果看是否是你想要效果
这里主要使用canvas 的drawImage方法
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
参数 | 描述 |
---|---|
img | 规定要使用的图像、画布或视频。 |
sx | 可选。开始剪切的 x 坐标位置。 |
sy | 可选。开始剪切的 y 坐标位置。 |
swidth | 可选。被剪切图像的宽度。 |
sheight | 可选。被剪切图像的高度。 |
x | 在画布上放置图像的 x 坐标位置。 |
y | 在画布上放置图像的 y 坐标位置。 |
width | 可选。要使用的图像的宽度。(伸展或缩小图像) |
height | 可选。要使用的图像的高度。(伸展或缩小图像) |
原理及用法(下方有完整代码组件)
1、首先 创建 画布
使用 id绑定
<canvas :id="`circle(1)${index}`"></canvas>
2、获取画布 获取绘图工具
const cvs = document.getElementById('circle(1)'+this.index);
const ctx = cvs.getContext("2d");
3、获取需要绘制的图片
首先先创建图片,再使用canvas绘制出来
注意点:由于图片加载需要时间 是异步的,所以需要等图片加载完成创建dom后,canvas才能绘制出来
const img = document.createElement("img");
img.src = this.list.img;
ctx .drawImage(img,0,0);
这里我给出参考图片
4、使用定时器setInterva进行刷新 产生帧动画
注意点:定时刷新时 如果canvas 没有设置宽度和高度会出现重影效果,因为重新设置宽度和高度相当于重绘了,关闭页面时记得清理定时器
问题效果
setInterval(() => {
// 重绘 宽高 防止重影
cvs.width = radius * 2;
cvs.height = radius * 2;
// 或者使用 clearRect() 方法清空给定矩形内的指定像素
// ctx.clearRect(0, 0, radius * 2, radius * 2);
},20)
5、旋转 及计算出旋转的角速度
使用ctx.rotate();进行旋转
注意点1:旋转时需要设置偏移,保证为中心旋转,先偏移再旋转后在纠正回来。
注意点2:旋转了多少 后面就要 反旋转回来 不然会导致旋转累加,实际效果和最终效果会产生有很大的区别
计算旋转角速度(1°/s):转换为rad/s及(1度/秒),定时器设置的刷新时间为fps(毫秒) ,1°=π/180,设置speed单位为(1°/s),最终可以算出 旋转度数= speed*fps/10000
ctx.translate(radius, radius);
ctx.rotate(rotate);
ctx.translate(-radius, -radius);
ctx.drawImage(img,0,0);
ctx.translate(radius, radius);
ctx.rotate(-rotate);
ctx.translate(-radius, -radius);
6、画布上居中
context.drawImage();方法有三种写法
//1
context.drawImage(img,sx,sy);
//2
context.drawImage(img,sx,sy,swidth,sheight);
//3
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
图片在画布上居中需要使用第三种完整写法
计算居中
x = 画布宽度 /2 - 图片宽度 /2;
y = 画布高度 /2 - 图片高度 /2;
vue组件完整代码(供参考缺失部分css样式)
<template>
<div id="paralist" class="row">
<div class="list-left">
<canvas :id="`circle(1)${index}`"></canvas>
<!-- <div>
<span class="span-mini">{
{
this.list.parameter}}</span>
</div> -->
</div>
<!-- <div class="list-right">
<div class="normal" v-if="this.list.status=='正常'">
<span class="span-mini">正常</span>
</div>
<div class="unusual" v-else-if="!(this.list.status=='正常')">
<span class="span-mini">
{
{
this.list.describe}}
<br />
<span class="unusual-bottom span-mini" @click="this.popup">处理</span>
</span>
</div>
</div> -->
</div>
</template>
<script>
export default {
props: ["list", "index"],
data() {
return {
// isShow:true,
angleTimer: "",
radius: 105 / 2,
imgModule: [
{
src: this.list.img,
dom: "", // 储存img 生成的dom
distance: 0,
speed: 0, // 旋转速度 r/s speed*( Math.PI / 180)/(fps/1000) 单位 弧度/秒 (rad/s)。(1rad = 360°/(2π) ≈ 57°17'45″)
angle: 0, // 旋转方向 1为顺时针, -1为逆时针 0为不旋转
rotate: 0, // 开始旋转角度
sx: 0, // 开始剪切位置 x
sy: 0, // 开始剪切位置 y
swidth: this.radius * 2, // 被剪切图像宽度
sheight: this.radius * 2, // 被剪切图像高度
x: "", // 画布在图像x 坐标位置
y: "", // 画布在图像y 坐标位置
width: this.radius * 2, // 要使用图像的宽度
height: this.radius * 2, // 要使用图像的高度
},
{
src: require("@/assets/images2/circle1/资源 88.png"),
dom: "", // 储存img 生成的dom
distance: 0,
speed: 0.5 * 360, // 旋转速度 r/s speed*( Math.PI / 180)/(fps/1000) 单位 弧度/秒 (rad/s)。(1rad = 360°/(2π) ≈ 57°17'45″)
angle: 1, // 旋转方向 1为顺时针, -1为逆时针 0为不旋转
rotate: 0, // 开始旋转角度
sx: 0, // 开始剪切位置 x
sy: 0, // 开始剪切位置 y
swidth: this.radius * 2, // 被剪切图像宽度
sheight: this.radius * 2, // 被剪切图像高度
x: "", // 画布在图像x 坐标位置
y: "", // 画布在图像y 坐标位置
width: this.radius * 2, // 要使用图像的宽度
height: this.radius * 2, // 要使用图像的高度
},
{
src: require("@/assets/images2/circle1/资源 87.png"),
dom: "", // 储存img 生成的dom
distance: 0,
speed: 0, // 旋转速度 r/s speed*( Math.PI / 180)/(fps/1000) 单位 弧度/秒 (rad/s)。(1rad = 360°/(2π) ≈ 57°17'45″)
angle: 0, // 旋转方向 1为顺时针, -1为逆时针 0为不旋转
rotate: 0, // 开始旋转角度
sx: 0, // 开始剪切位置 x
sy: 0, // 开始剪切位置 y
swidth: this.radius * 2, // 被剪切图像宽度
sheight: this.radius * 2, // 被剪切图像高度
x: "", // 画布在图像x 坐标位置
y: "", // 画布在图像y 坐标位置
width: this.radius * 2, // 要使用图像的宽度
height: this.radius * 2, // 要使用图像的高度
},
{
src: require("@/assets/images2/circle1/资源 86.png"),
dom: "", // 储存img 生成的dom
distance: 0,
speed: 0.2 * 360, // 旋转速度 r/s speed*( Math.PI / 180)/(fps/1000) 单位 弧度/秒 (rad/s)。(1rad = 360°/(2π) ≈ 57°17'45″)
angle: -1, // 旋转方向 1为顺时针, -1为逆时针 0为不旋转
rotate: 0, // 开始旋转角度
sx: 0, // 开始剪切位置 x
sy: 0, // 开始剪切位置 y
swidth: this.radius * 2, // 被剪切图像宽度
sheight: this.radius * 2, // 被剪切图像高度
x: "", // 画布在图像x 坐标位置
y: "", // 画布在图像y 坐标位置
width: this.radius * 2, // 要使用图像的宽度
height: this.radius * 2, // 要使用图像的高度
},
],
};
},
mounted() {
this.draw("circle(1)" + this.index);
},
beforeDestroy() {
if (this.angleTimer) {
clearInterval(this.angleTimer);
}
},
methods: {
// 点击进入窗口
routerTo() {
this.$store.commit("setIsBack", true);
this.$router.push({
path: "/dw/equipmentAlert",
});
},
popup() {
// console.log(this.list);
alert("弹窗!");
},
/**
* id 渲染canvas id
* fps 定时刷新 时间
*/
draw(id, fps = 20) {
const radius = this.radius;
const imgModule = this.imgModule;
const img = document.createElement("img");
img.src = this.list.img;
imgModule.forEach((item) => {
item.dom = document.createElement("img");
item.dom.src = item.src;
});
const cvs = document.getElementById(id);
const ctx = cvs.getContext("2d");
if (this.angleTimer) {
clearInterval(this.angleTimer);
}
this.angleTimer = setInterval(() => {
// 重绘 宽高 防止重影
cvs.width = radius * 2;
cvs.height = radius * 2;
let rotateTotal = 0;
// ctx.clearRect(0, 0, radius * 2, radius * 2);
imgModule.forEach((item, index) => {
item.distance += (item.speed * fps) / 1000; // 旋转总距离
if (item.distance % 360 == 0) {
item.distance = 0;
}
let rotate =
(-item.rotate * Math.PI) / 180 +
(item.angle * item.distance * Math.PI) / 180;
rotateTotal += rotate;
let naturalWidth = item.dom.naturalWidth; //图片原始宽度
let naturalHeight = item.dom.naturalHeight; //图片原始高度
ctx.translate(radius, radius);
ctx.rotate(rotate);
ctx.translate(-radius, -radius);
ctx.drawImage(
item.dom,
item.sx || 0,
item.sy || 0,
item.swidth || cvs.width,
item.sheight || cvs.height,
(item.x = (radius * 2) / 2 - naturalWidth / 2 || 0), //居中
(item.y = (radius * 2) / 2 - naturalHeight / 2 || 0), //居中
item.width || cvs.width,
item.height || cvs.height
);
// ctx.rotate(-rotate);
// 注意! 旋转了多少 后面就要 反旋转回来
// 不然会导致旋转累加,实际效果和最终效果会产生有很大的区别
ctx.translate(radius, radius);
ctx.rotate(-rotate);
ctx.translate(-radius, -radius);
});
}, fps);
},
},
};
</script>
<style lang="less" scoped>
.row {
background: transparent;
margin-top: 0;
}
#paralist {
width: 220px;
min-width: 220px;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.list-left {
flex: 1;
img {
max-width: 105px;
max-height: 113px;
// background-color: red;
}
div {
box-sizing: border-box;
span {
font-size: 15pt;
color: #def5fe;
}
}
}
.list-right {
flex: 1;
height: 100%;
position: relative;
.normal {
// line-height: 100%; //不知为何无法居中
height: 100%;
position: relative;
span {
font-size: 14pt;
position: absolute;
left: 10%;
top: 50%;
transform: translateY(-100%);
}
}
.unusual {
position: absolute;
top: 10%;
text-align: left;
span {
display: inline-block;
font-size: 8pt;
color: red;
}
.unusual-bottom {
color: hsl(190, 99%, 51%);
border-bottom: 1px solid #06d6fe;
cursor: pointer;
}
}
}
}
</style>