Canvas画板 带暂停加速回放减速功能
写这个的原因是支付宝写福字可以回放,就自己写一个试试。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>画板</title>
</head>
<style>
#box {
width: 500px;
height: 500px;
border: 12px solid #eee;
border-radius: 10px;
position: relative;
}
#Toolbar {
width: 100%;
height: 100px;
display: flex;
justify-content: space-around;
align-items: center;
}
#Toolbar p {
width: 100px;
margin: 0;
padding: 0;
text-align: center;
cursor: pointer
}
#sketchpad {
width: 100%;
height: 400px;
box-shadow: 0 0 0 2px #eee;
position: relative;
}
#canvas {
cursor: url(./image/pen.png)3 16, pointer;
}
#play_box{
position: absolute;
top: 0;
right: -70px;
display: flex;
flex-direction: column;
}
#play_box > button{
margin-top: 10px;
border:none;
padding: 2px;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
width: 50px;
height: 50px;
border-radius: 30%;
outline:medium;
box-shadow:0 0 8px 0 #534d4e;
transition-property: box-shadow;
transition-duration: 1s;
transition-timing-function: linear;
}
#play_box > button:hover{
box-shadow:0 0 8px 0 #274ee5;
}
</style>
<body>
<div id="box">
<div id="Toolbar">
<p tyle="pencil">铅笔
<input type="color">
</p>
<label for="num">加粗
<input id="num" type="number" max="50" style="width: 100px;">
</label>
<p tyle="Draw">画圆</p>
<p tyle="del">擦除</p>
</div>
<div id="sketchpad">
<canvas id="canvas" width="500" height="400"></canvas>
</div>
<div id="play_box">
<button id="play_back">回放</button>
<button id="play_stop">暂停</button>
<button id="play_go_on">继续</button>
<button id="play_accelerated">加速</button>
<button id="play_slowdown">减速</button>
</div>
</div>
</body>
</html>
<script>
// 方法类
class Method {
// 擦除方法
del_canvas(e) {
let {
x,
y
} = tb.start;
tb.ctx.moveTo(x, y);
tb.ctx.strokeStyle = "#fff";
tb.ctx.lineTo(e.offsetX, e.offsetY);
tb.ctx.stroke();
tb.start.x = e.offsetX;
tb.start.y = e.offsetY;
tb.canvas.style.cursor = "grabbing";
}
// 画圆
draw_canvas_circle(e) {
let {
x,
y
} = tb.start;
tb.ctx.beginPath();
tb.ctx.arc(x, y, tb.arc_width, 50, 0, 2 * Math.PI);
tb.ctx.stroke();
canvas_playback.set_path([
[x, y],
[tb.arc_width], tb.Pencil_Color, tb.lineWidth
]); //添加绘制路径到canvas_playback类存储
if(tb.arc_width === 0){
tb.arc_start = e.x;
++tb.arc_width;
}
if(tb.arc_start < e.x){
++tb.arc_width;
tb.arc_start = e.x;
}
if(tb.arc_start > e.x){
--tb.arc_width;
tb.arc_start = e.x;
}
}
// 画线
draw_canvas(e) {
let {
x,
y
} = tb.start;
tb.ctx.beginPath();
tb.ctx.moveTo(x, y);
tb.ctx.strokeStyle = tb.Pencil_Color;
tb.ctx.lineWidth = tb.lineWidth;
tb.ctx.lineTo(e.offsetX, e.offsetY);
tb.ctx.stroke();
canvas_playback.set_path([
[x, y],
[e.offsetX, e.offsetY], tb.Pencil_Color, tb.lineWidth
]); //添加绘制路径到canvas_playback类存储
tb.start.x = e.offsetX;
tb.start.y = e.offsetY;
}
}
class Toolbar extends Method {
constructor(id) {
super();
this.Toolbar_func = new Map(); // 存储工具栏的方法使用 Map(key ,func) 存储
this.Pencil_Color = "red";//默认颜色
this.lineWidth = 1;//默认宽度
this.current_func = "pencil"; //当前选中的工具栏
this.Toolbar_P = Array.from(document.querySelectorAll(`${id} > p`));
this.canvas = document.querySelector("#canvas");
this.canvas.addEventListener("mousedown", this.canvas_mousedown.bind(this));
this.canvas.addEventListener("mouseup", this.stop_mousemove.bind(this));
this.init(this.Toolbar_P);
this.ctx = this.canvas.getContext("2d");
this.start = {
x: 0,
y: 0,
}
this.lineTo = {
lineTo_x: 0,
lineTo_y: 0,
}
this.arc_width = 0;
this.arc_start = 0;
this.is_mouseup = false
};
// 初始化切换
init(dom) {
for (let i in dom) {
let tyle = dom[i].getAttribute("tyle");
this.Toolbar_func.set(tyle, " ");
dom[i].addEventListener("click", function tab() {
tb.current_func = this.getAttribute('tyle');
this.style.boxShadow = "0 2px 0 0 #999";
this.style.transition = "box-Shadow 1s";
for (let s in dom) {
if (dom[s] !== this) {
dom[s].style.boxShadow = "initial"
} else {
if (dom[s].getAttribute("tyle") === "del") {
document.querySelector("#canvas").style.cursor = "grabbing";
} else {
document.querySelector("#canvas").style.cursor =
"url(./image/pen.png)3 16, pointer";
}
}
}
})
}
// 添加工具栏的方法
this.Toolbar_func.set("pencil", super.draw_canvas);
this.Toolbar_func.set("Draw", super.draw_canvas_circle);
this.Toolbar_func.set("del", super.del_canvas);
};
// 添加画布的点击事件,点击画布进行画画
canvas_mousedown(e) {
this.is_mouseup = false;
this.start.x = e.offsetX;
this.start.y = e.offsetY;
this.canvas.addEventListener("mousemove", this.canvas_drawn)
};
// 结束鼠标事件
stop_mousemove() {
this.arc_width = 0;
this.canvas.removeEventListener("mousemove", this.canvas_drawn);
}
// 绘制的方法
canvas_drawn(e) {
if (!!tb.current_func) {
tb.Toolbar_func.get(tb.current_func)(e);
}
}
// 修改笔的颜色
Modify_Pencil_Color(type) {
this.Pencil_Color = type ? type : "#000";
}
// 修改线的宽度
Modify_line_Width(width) {
if (!Number(width)) {
this.lineWidth = 1;
throw "参数错误";
}
this.lineWidth = width;
}
// 添加操作栏的方法
add_Toolbar_func(name, func, iscover = false) {
if (!!name && !!func && Object.prototype.toString.call(name) !== "[object String]" && Object.prototype
.toString.call(func) !== "[object function]") {
throw "参数不全或类型错误";
return;
}
let is_has = this.Toolbar_func.has(name);
if (is_has && !iscover) {
throw "方法存在禁止重写,设置形参iscover 为 true可重写"
return;
}
this.Toolbar_func.set(name, func);
}
}
var tb = new Toolbar("#Toolbar");
document.querySelector("[type=color]").addEventListener("change", function (e) {
let color = this.value;
tb.Modify_Pencil_Color(color);
})
document.querySelector("[type=number]").addEventListener("change", function (e) {
let width = this.value;
tb.Modify_line_Width(width);
})
// 画画回放
class Canvas_Playback {
constructor() {
this.canvas_Path = new Map();
this.stop = true;
this.speed = 200;
}
set_path(path) {
this.canvas_Path.set(this.canvas_Path.size + 1, path);
};
get_path() {
return this.canvas_Path;
}
Playback() {
let map_path = this.canvas_Path;
tb.ctx.clearRect(0, 0, 500, 400);
let lenth = map_path.size;
let that = this;
let a = 1;
if (lenth === 0) {
alert("无可回放的图画");
return;
};
setTimeout(function go_play(){
let p;
if(a === lenth){
clearTimeout(p);
return;
}
let i = map_path.get(a);
if(i[1].length === 1){
tb.ctx.beginPath();
tb.ctx.strokeStyle = i[2];
tb.ctx.arc(i[0][0], i[0][1], i[1][0], 50, 0, 2 * Math.PI);
tb.ctx.stroke();
}else{
tb.ctx.beginPath();
tb.ctx.moveTo(i[0][0], i[0][1]);
tb.ctx.strokeStyle = i[2];
tb.ctx.lineWidth = i[3];
tb.ctx.lineTo(i[1][0], i[1][1]);
tb.ctx.stroke();
}
that.stop && ++a;
p = setTimeout(go_play,that.speed)
},100)
}
check(msg){
let map_path = this.canvas_Path.size;
if(map_path === 0){
alert(msg);
return false;
}
return true;
}
play_stop(){
if(this.check("没有画可操作")) this.stop = false;
}
play_go_on(){
if(this.check("没有画可操作")) this.stop = true;
}
play_accelerated(){
if(this.check("没有画可操作")) this.speed > 0 ? this.speed -= 100 : this.speed = 0;
}
play_slowdown(){
if(this.check("没有画可操作")) this.speed += 100;
}
}
var canvas_playback = new Canvas_Playback();
document.querySelector("#play_box").addEventListener("click",function(e){
let id = e.target.getAttribute("id");
switch(id){
case "play_back":
canvas_playback.Playback();
break;
case "play_stop":
canvas_playback.play_stop();
break;
case "play_go_on":
canvas_playback.play_go_on();
break;
case "play_accelerated":
canvas_playback.play_accelerated();
break;
case "play_slowdown":
canvas_playback.play_slowdown();
break;
default:
console.log(`无此方法${id}`);
}
},true)
</script>