背景:
准备写一个桌面鼠标轨迹、事件可视化插件的,写着写着发现带点游戏效果挺好的。
要用JS写面向对象的代码,我个人认为最好的场景一定是游戏,特别适用于canvas。
写得仓促,关于代码不足的还望各位不吝赐教。
🫠、效果演示
一、程序入口
项目目录:
+[js]文件夹
-fun.js //存放用到的自定义函数
-自定义类.js //存放自定义类
-main.js //存放主入口函数
+index.html
二、程序主要目标
1.每秒60次动画循环
使用浏览器 window.requestAnimationFrame
设置每秒60次动画循环。创建的对象都要放到自定义函数animate
内部绘制,每次绘制前使用c2d.clearRect
清除画布。
animate();//循环动画函数
function animate(t){ //t表示当前时间 毫秒 一直在增加
nowTime = t //暂未用到
c2d.clearRect(0,0,canvas.width,canvas.height);
//设置网格线
tool.drawGrid(c2d,网格宽,网格高);
画十字线(鼠标.x, 鼠标.y)
for(let one of arr){
one.draw(c2d)
}
requestAnimationFrame(animate);//每隔16.67ms去调用
}
2.绘制网格线
调用:
let tool = new Tool();//创建一个工具人
const 网格宽 = 网格高 = 10
//... ...此处省略其他代码,下同
function animate(t){
//... ...
tool.drawGrid(c2d,网格宽,网格高);
//... ...
}
定义:
class Tool{ /* 声明一个工具类 */
//绘制网格
drawGrid(c2d,sizeX=10,sizeY=10,color="lightgray"){
c2d.save();//新建一个绘图状态
//设置一些参数
c2d.strokeStyle = color;//设置线条颜色
c2d.lineWidth = 0.5; //绘制线条的宽度
//绘制水平辅助线
for(let x = sizeX+0.5; x<c2d.canvas.width ; x+=sizeX ){
c2d.beginPath();//开始一个独立的路径
c2d.moveTo(x,0);
c2d.lineTo(x,c2d.canvas.height);
c2d.stroke();
}
//绘制垂直辅助线
for(let y = sizeY+0.5; y<c2d.canvas.height ; y+=sizeY ){
c2d.beginPath();//开始一个独立的路径
c2d.moveTo(0,y);
c2d.lineTo(c2d.canvas.width,y);
c2d.stroke();
}
c2d.restore();//返回"新建sava()"之前的画布状态
}//end drawGrid
}
3.绘制跟随鼠标移动的十字线
首先需要获得鼠标的坐标,结合canvas的最大宽度、高度可以知道两条交叉线两端的坐标。
3.1 获取鼠标位置
定义鼠标坐标的全局变量,在网页中获取鼠标相对于canvas
元素的精确坐标。
let 鼠标 = { //定义鼠标的坐标
x:0,
y:0
}
function 更新鼠标坐标(e){
let rect = canvas.getBoundingClientRect();
鼠标.x = e.clientX - rect.left;
鼠标.y = e.clientY - rect.top;
}
3.2 绘制十字线和交叉点坐标
默认交叉点坐标绘制在鼠标右上角,到边缘需要调整坐标显示值,做个边缘顶部和右侧边缘检测。
//文件fun.js
function 画十字线(x, y) {
// 绘制十字线
c2d.save()
let text = `( ${Math.round(x)} , ${Math.round(y)} )`
let textSize = c2d.measureText(text);
let textX = x + 10; // 右偏移量以离开鼠标点
let textY = y - 10; // 上偏移量以放在鼠标点上方
//判断 如果 x 靠近右侧边缘 , y靠近上册边缘
if(x >= canvas.width -80){
textX = x - 80
}
if(y < 50){
textY = y + 30
}
c2d.fillStyle = 'red';// 设置文本颜色为红色
c2d.fillText(text, textX, textY);
// 设置十字线的颜色和粗细
c2d.strokeStyle = '#FF0000'; // 示例颜色为红色
c2d.lineWidth = 1; // 示例设置更明显的线宽
// 水平线
c2d.beginPath();
c2d.moveTo(x, 0);
c2d.lineTo(x, canvas.height);
c2d.stroke();
// 垂直线
c2d.beginPath();
c2d.moveTo(0, y);
c2d.lineTo(canvas.width, y);
c2d.stroke();
c2d.restore()
}//画十字线
4.鼠标点击时绘制圆圈
当鼠标左键按下时绘制圆点,抬起时也绘制圆点。是为了绘制线时两端都有圆点。
为了防止圆重复绘制在同一个地方,代码中采用了一些判断,
先根据 name
获得 全局arr
绘图对象数组中的圆圈,然后通过 数组.some
进行判断。
调用:
let arr = [] //存放全局绘图对象 ,包括 圆点、线段、子弹等
canvas.addEventListener('mousedown', (e)=>{
线段.画 = true
更新鼠标坐标(e)
增加圆点() // ********此处调用***********
线段.x = 鼠标.x
线段.y = 鼠标.y
canvas.addEventListener('mouseup',(e)=>{
增加圆点() // ********此处调用***********
创建子弹()
线段.画 = false
线段.x = 鼠标.x
线段.y = 鼠标.y
})
});
定义:
4.按住鼠标左键画线
定义:
let 线段 = { //定义一个 线段 起点(x,y) 终点(x2,y2)
画:false, //表示是否开始画一个线段
x:0,
y:0,
x2:0,
y2:0,
}
设置一个线段的数据(两个端点),鼠标画线就是不断地连接两个点,然后记录下终点作为第二个线段的起点。
如果漏掉了赋值,就会出现下图的效果。
当鼠标按下,开始画线,获得第一个起点(x,y)
当鼠标移动,获得第二个起点(x2,y2),存入全局arr,并且将鼠标位置覆盖(x,y) ,作为第二段线段起点
当鼠标抬起,结束画线。
很有微积分的感觉,极小的线段不断相连组成连贯的曲线。
5.绽放烟花效果
当点击鼠标左键时,以鼠标位置创建速度一样的点对象。
定义:
三、面向对象程序总结
定义一个父类W, 子类 子弹、线、圆点继承父类,都有各自的绘图方法draw。
程序还有值得优化的地方,欢迎愿大佬
class W{
constructor(x,y,name='名字') {
this.x = x;
this.y = y;
this.name = name
//自身动画相关的
this.i = -1;//表示裁剪的图片位置
this.imgW = 80;//裁剪的宽度
this.sx = 0;//裁剪的起始x坐标
this.t = 0; //存放上一次图片切换的时间
}
}//结束 物体类
class 子弹 extends W{
constructor(x,y,angle,v=1){
super(x,y,'子弹')
this.angle = angle
this.v = v
}
move(){
this.x += Math.cos(this.angle) * this.v;
this.y += Math.sin(this.angle) * this.v;
}
draw(c2d){
this.move()
//绘图方法
}
}
//创建一个线条类
class 线 extends W{
constructor(x,y,x2,y2){
super(x,y,'线段')
this.x2 = x
this.y2 = y2
}
draw(c2d){
//绘图方法
}
}
// 绘制一个圆圈
class 圆点 extends W{
constructor(x,y){
super(x,y,'圆点')
}
draw(c2d){
//绘图方法
}
}
原文链接:https://juejin.cn/post/7334311273340239887 作者:百万蹄蹄向前冲