canvas-getContext("2d")实例2-射箭游戏

效果展示:

html+js代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>bezierCurveTo</title>
    <style>
        .canvas{
            display: inline-block;
            float: left;
            width: 300px;
            margin: 10px;
        }
        canvas {
            border:1px solid #d3d3d3;
        }
    </style>
</head>
<body>
    <div class="canvas">
        <div>------------射箭游戏-----------</div>
        <canvas id="myCanvas" width="900" height="300"></canvas>
    </div>
    <script>
        // 射箭游戏
        class ShotAnArrow {
            constructor(id){
                let idNode = document.getElementById(id);
                this.id = idNode;
                this.width = idNode.width;
                this.height = idNode.height;
                this.ctx = idNode.getContext('2d');
                this.top = idNode.getBoundingClientRect().top;
                this.left = idNode.getBoundingClientRect().left;
                this.startX = 700;
                this.startY = 100;
                this.startText = '';

                this.endX = 700;
                this.endY = 200;
                this.endText = '';

                this.controlX = 700;
                this.controlY = 150;
                this.controlText = '';

                this.pointName = '';


                // 箭的属性值
                this.arrowEndX = 700;
                this.arrowEndY = 150;
                this.arrowStartX = this.arrowEndX - Math.cos(this.angle / 180 * Math.PI) * 100;
                this.arrowStartY = this.arrowEndY + Math.sin(this.angle / 180 * Math.PI) * 100;
                this.fly = false; // 箭头是否释放
                this.angle = 0;

                // 分数面板属性
                this.score = 0;

                this.mouseupfn = this.mouseupfn.bind(this);
                this.mousemovefn = this.mousemovefn.bind(this);
                this.mousedownfn = this.mousedownfn.bind(this);
                this.getMousePos = this.getMousePos.bind(this);
                this.sampleMousemovefn = this.sampleMousemovefn.bind(this);
            }

            init() {
                this.ctx.clearRect(0,0,this.width,this.height);
                this.drawLine();
                this.drawTarget();
                this.drawthreeArc();
                this.dragFn();
                this.drawBow();
                this.drawArrow();
                this.drawEnergySolid();
                this.drawScore();
            }

            // 绘制贝塞尔曲线 以及控制点到贝塞尔曲线两端的直线
            drawLine() {
                let {startX, startY, endX, endY, controlX, controlY} = this;
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'pink';
                this.ctx.lineWidth = 1;
                this.ctx.moveTo(startX, startY);
                this.ctx.quadraticCurveTo(controlX, controlY, endX, endY);
                this.ctx.stroke();

                this.ctx.beginPath();
                this.ctx.strokeStyle = 'pink';
                this.ctx.lineWidth = 1;
                this.ctx.moveTo(startX, startY);
                this.ctx.lineTo(controlX, controlY);
                
                this.ctx.stroke();

                this.ctx.beginPath();
                this.ctx.strokeStyle = 'pink';
                this.ctx.lineWidth = 1;
                this.ctx.moveTo(endX, endY);
                this.ctx.lineTo(controlX, controlY);
                this.ctx.stroke();
            }

            // 控制点增加圆环样式
            drawArc(x, y, r, lineWidth) {
                this.ctx.beginPath();
                this.ctx.strokeStyle = "#999";
                this.ctx.lineWidth = 1;
                this.ctx.arc(x,y,r,0,2*Math.PI);
                // if (lineWidth) this.ctx.lineWidth = lineWidth;
                this.ctx.fillText("("+ x +","+ y +")", x + 10, y);
                this.ctx.stroke();
            }

            // 绘制三个控制点 直线的开始点 结束点 贝塞尔曲线的控制点
            drawthreeArc(){
                let {controlX, controlY} = this;
                this.drawArc(controlX, controlY, 6);
            }

            //获取鼠标在画布中的绝对位置
            getMousePos(evt){
                let rect = this.id.getBoundingClientRect();
                return {
                    x: evt.clientX - rect.left * (this.width / rect.width),
                    y: evt.clientY - rect.top * (this.height / rect.height)
                }
            }

            // 点被选中后的鼠标移动事件
            mousemovefn(evt){
                let {x, y} = this.getMousePos(evt);
                let {top, left} = this;
                let {startX, startY, endX, endY, controlX, controlY} = this;
                if (x > controlX - 10 && y > controlY - 10 && x < controlX + 10 && y < controlY + 10){
                    this.pointName = 'control';
                    this.id.style.cssText = "cursor: pointer;";
                    this.drawArc(controlX, controlY, 8, 1);
                    this.controlX = evt.clientX - left;
                    this.controlY = evt.clientY - top;
                    this.avoidStartAndEndPointOverlay();
                    this.fly = false;
                    this.init();
                }else {
                    this.pointName = '';
                    this.id.style.cssText = "cuosor: default;";
                    this.fly = true;
                    this.init();
                }
            }

            // 避免三个控制点位置重叠
            avoidStartAndEndPointOverlay(){
                let {startX, startY, endX, endY, controlX, controlY} = this;
                if (Math.abs(startX - controlX) < 5 || Math.abs(startY - controlY) < 5) {
                    if (this.pointName === 'start') {
                        this.startX += 5;
                        this.startY += 5;
                    }
                    if (this.pointName === 'control') {
                        this.controlX += 5;
                        this.controlY += 5;
                    }
                }

                if (Math.abs(endX - controlX) < 5 || Math.abs(endY - controlY) < 5) {
                    if (this.pointName === 'end') {
                        this.endX += 5;
                        this.endY += 5;
                    }
                    if (this.pointName === 'control') {
                        this.controlX += 5;
                        this.controlY += 5;
                    }
                }
            }

            // 鼠标移动事件。当鼠标移动到控制点时控制点增加样式
            sampleMousemovefn(evt){
                let {x, y} = this.getMousePos(evt);
                let {top, left} = this;
                let {startX, startY, endX, endY, controlX, controlY} = this;
                if (x > controlX - 10 && y > controlY - 10 && x < controlX + 10 && y < controlY + 10){
                    this.id.style.cssText = "cursor: pointer;";
                    this.drawArc(controlX, controlY, 6, 1);
                }else {
                    this.id.style.cssText = "cuosor: default;";
                }
            }

            mousedownfn(){
                // 重置各项参数
                this.startX = 700;
                this.startY = 100;
                this.startText = '';

                this.endX = 700;
                this.endY = 200;
                this.endText = '';

                this.controlX = 700;
                this.controlY = 150;
                this.controlText = '';

                this.pointName = '';


                // 箭的属性值
                this.arrowEndX = 700;
                this.arrowEndY = 150;
                this.arrowStartX = this.arrowEndX - Math.cos(this.angle / 180 * Math.PI) * 100;
                this.arrowStartY = this.arrowEndY + Math.sin(this.angle / 180 * Math.PI) * 100;
                this.fly = false; // 箭头是否释放
                this.angle = 0;

                // 分数面板属性
                this.score = 0;
                this.id.addEventListener("mousemove", this.mousemovefn, false);
            }

            mouseupfn(){
                this.fly = true;
                this.id.removeEventListener("mousemove", this.mousemovefn, false);
                this.controlXStatic = this.controlX;
                this.controlYStatic = this.controlY;
                this.controlAnimation();
                this.arrawAnimation();
            }

            // 控制点被释放后的动画
            controlAnimation(){
                let {controlX, controlY} = this;
                let speedX = 5, speedY = 5 * (controlY - 150) / (controlX - 700);
                let controlTimer = setInterval(()=>{
                    this.controlX = (this.controlX - speedX).toFixed(2);
                    this.controlY = (this.controlY - speedY).toFixed(2);
                    if(this.controlX <= 700){
                        clearInterval(controlTimer);
                        controlTimer = null;
                        this.controlX = 700;
                        this.controlY = 150;
                    }
                },30);
            }

            // 圆点拖拽功能
            dragFn(){
                this.id.removeEventListener('mousemove', this.sampleMousemovefn, false);
                this.id.removeEventListener('mousedown', this.mousedownfn, false);
                this.id.removeEventListener('mouseup', this.mouseupfn, false);
                this.id.addEventListener("mousemove", this.sampleMousemovefn, false);
                this.id.addEventListener("mousedown", this.mousedownfn, false);
                this.id.addEventListener("mouseup", this.mouseupfn, false);
            }

            // 绘制弓的形状
            drawBow(){
                this.ctx.beginPath();
                this.ctx.lineCap = 'round';
                this.ctx.moveTo(700,95);
                this.ctx.lineTo(700, 100);
                this.ctx.quadraticCurveTo(650, 100, 650, 150);
                this.ctx.quadraticCurveTo(650, 200, 700, 200);
                this.ctx.lineTo(700, 205);
                this.ctx.lineWidth = 5;
                this.ctx.strokeStyle='#795548';
                this.ctx.stroke();
                this.ctx.beginPath();
                this.ctx.lineWidth=4;
                this.ctx.moveTo(646, 144);
                this.ctx.lineTo(646, 156);
                this.ctx.stroke();
            }

            // 绘制箭
            drawArrow(){
                this.ctx.beginPath();
                // controlY controlX
                if(this.fly){

                }else {
                    this.arrowEndX = this.controlX;
                    this.arrowEndY = this.controlY;
                }

                this.arrowStartX = this.arrowEndX - Math.cos(this.angle / 180 * Math.PI) * 100;
                this.arrowStartY = this.arrowEndY + Math.sin(this.angle / 180 * Math.PI) * 100;

                this.ctx.lineWidth = 1;
                this.ctx.strokeStyle = 'red';
                this.ctx.moveTo(this.arrowEndX, this.arrowEndY);
                this.ctx.lineTo(this.arrowStartX, this.arrowStartY);
                
                this.ctx.stroke();

                this.ctx.beginPath();
                this.ctx.lineWidth = 3;
                this.ctx.strokeStyle = 'red';
                // this.ctx.moveTo(this.arrowStartX + Math.cos((90 + this.angle) / 180) * 10, this.arrowStartY - Math.sin((90 + this.angle) / 180) *8);
                // this.ctx.lineTo(this.arrowStartX, this.arrowStartY);
                // this.ctx.lineTo(this.arrowStartX + Math.cos((this.angle+135) / 180) * 10, this.arrowStartY + Math.sin((this.angle+135) / 180) *8)
                this.ctx.arc(this.arrowStartX, this.arrowStartY, 4, 0, 2 * Math.PI);
                
                this.ctx.stroke();
            }
            // 箭的飞行动画
            arrawAnimation(){
                let {controlXStatic, controlYStatic} = this;
                let rate = (controlXStatic - 700) / 100 > 1?1:(controlYStatic - 700) / 100;
                // 初始水平速度
                let vx = rate * 70;
                let time = 0; // 箭的飞行时间
                let arrowTimer = setInterval(()=>{
                    // 水平移动量
                    let s = vx * time;
                    // 竖直移动量
                    let H = 9.8 * time * time / 2;

                    this.angle = Math.atan(9.8 * time / vx ) * 180 > 90 ? 85 :  Math.atan(9.8 * time / vx ) * 180 ;

                    // 更新箭结束点的位置
                    this.arrowEndX -= s;
                    this.arrowEndY += H;
                    if (this.arrowStartX < 100) {
                        clearInterval(arrowTimer);
                        arrowTimer = null;

                        this.arrowStartX = 100;
                        this.arrowStartY = Math.sin(this.angle / 180 * Math.PI) * 100 + 9.8 * time * time / 2 + this.controlYStatic;
                        this.arrowEndX = 100 + Math.cos(this.angle / 180 * Math.PI) * 100;
                        this.arrowEndY = 9.8 * time * time / 2 + this.controlYStatic;

                        this.controlX = 700;
                        this.controlY = 150;

                        this.pointName = '';
                        this.id.style.cssText = "cuosor: default;";
                        this.getScore();
                        this.init();
                        return ;
                    }
                    if (this.arrowStartY > 300) {
                        clearInterval(arrowTimer);
                        arrowTimer = null;
                        this.controlX = 700;
                        this.controlY = 150;
                        this.pointName = '';
                        this.id.style.cssText = "cuosor: default;";
                        this.getScore();
                        this.init();
                        return;
                    }
                    this.init();
                    time += 0.1;
                }, 30);
            }

            // 获得射箭的分数
            getScore(){
                let {arrowStartY} = this;
                this.score = 100 * (1-(Math.abs(arrowStartY - 150) / 95)).toFixed(2) > 0 ? 100 * (1-(Math.abs(arrowStartY - 150) / 95)).toFixed(2) : 0;
            }

            // 绘制能量柱
            drawEnergySolid(){
                let {controlX, controlY} = this;
                let rate = (controlX - 700) / 100 > 1?1:(controlX - 700) / 100;
                let colorArr = ['#3988dc','#318e27','#c6d222', '#da8b12', '#ca2929']; // 能量越来越高
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'red';
                this.ctx.lineWidth = 1;
                this.ctx.strokeRect(850, 200, 50, 100);
                this.ctx.globalCompositeOperation = 'source-over';
                
                let gradient = this.ctx.createLinearGradient(875, 300 - 100 * rate, 875, 300);
                if (rate > 0 && rate < 0.25){
                    gradient.addColorStop(0,"#3988dc");
                    gradient.addColorStop(1,"#9cb0da");
                }else if (rate >= 0.25 && rate < 0.5){
                    gradient.addColorStop(0,"#318e27");
                    gradient.addColorStop(0.5,"#3988dc");
                    gradient.addColorStop(1,"#9cb0da");
                }
                else if (rate >=  0.5 && rate <  0.7){
                    gradient.addColorStop(0,"#c6d222");
                    gradient.addColorStop(0.3,"#318e27");
                    gradient.addColorStop(0.6,"#3988dc");
                    gradient.addColorStop(1,"#9cb0da");
                }
                else if (rate >=  0.7 && rate <  0.9){
                    gradient.addColorStop(0,"#da8b12");
                    gradient.addColorStop(0.25,"#c6d222");
                    gradient.addColorStop(0.5,"#318e27");
                    gradient.addColorStop(0.75,"#3988dc");
                    gradient.addColorStop(1,"#9cb0da");
                }else if (rate >=  0.9 && rate <= 1){
                    gradient.addColorStop(0,"#ca2929");
                    gradient.addColorStop(0.25,"#da8b12");
                    gradient.addColorStop(0.5,"#c6d222");
                    gradient.addColorStop(0.75,"#318e27");
                    gradient.addColorStop(0.9,"#3988dc");
                    gradient.addColorStop(1,"#9cb0da");
                }
                this.ctx.fillStyle = gradient;
                this.ctx.fillRect(850, 300 - 100 * rate, 50, 100 * rate);
            }

            // 绘制靶子
            drawTarget(){
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'red';
                this.ctx.lineWidth = 1;
                this.ctx.ellipse(100, 150, 5, 35, 0, 0, 2 * Math.PI);
                this.ctx.stroke();
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'red';
                this.ctx.lineWidth = 1;
                this.ctx.ellipse(100, 150, 15, 45, 0, 0, 2 * Math.PI);
                this.ctx.stroke();
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'red';
                this.ctx.lineWidth = 1;
                this.ctx.ellipse(100, 150, 25, 55, 0, 0, 2 * Math.PI);
                this.ctx.stroke();
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'red';
                this.ctx.lineWidth = 1;
                this.ctx.ellipse(100, 150, 35, 65, 0, 0, 2 * Math.PI);
                this.ctx.stroke();
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'red';
                this.ctx.lineWidth = 1;
                this.ctx.ellipse(100, 150, 45, 75, 0, 0, 2 * Math.PI);
                this.ctx.stroke();
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'red';
                this.ctx.lineWidth = 1;
                this.ctx.ellipse(100, 150, 55, 85, 0, 0, 2 * Math.PI);
                this.ctx.stroke();
                this.ctx.beginPath();
                this.ctx.strokeStyle = 'red';
                this.ctx.lineWidth = 1;
                this.ctx.ellipse(100, 150, 65, 95, 0, 0, 2 * Math.PI);
                this.ctx.stroke();
            }

            // 绘制分数面板
            drawScore(){
                this.ctx.beginPath();
                this.ctx.fillStyle = '#999';
                this.ctx.fillRect(800, 0, 100, 50);
                this.ctx.textAlign = "center";
                this.ctx.font = '20px Arial';
                this.ctx.fillStyle = 'red';
                this.ctx.fillText('得分:' + this.score, 850, 30);
                this.ctx.stroke();

                let time = 4 * (this.score / 100);
                let score = 0;
                let scoreTimer = setInterval(()=>{
                    this.ctx.clearRect(800, 0, 100, 50);
                    this.ctx.beginPath();
                    this.ctx.fillStyle = '#999';
                    this.ctx.fillRect(800, 0, 100, 50);
                    this.ctx.textAlign = "center";
                    this.ctx.font = '20px Arial';
                    this.ctx.fillStyle = 'red';
                    this.ctx.fillText('得分:' + score, 850, 30);
                    this.ctx.stroke();
                    score = score + 1;
                    if(score > this.score) {
                        clearInterval(scoreTimer);
                        scoreTimer = null;
                    }
                }, time);
            }
        }

        const myCanvas = new ShotAnArrow('myCanvas');
        myCanvas.init();
    </script>
</body>
</html>

猜你喜欢

转载自www.cnblogs.com/dadouF4/p/11733986.html