canvas[学习笔记三]

192 阅读1分钟

动画

1、小球弹性运动

<script>
    const canvas=document.getElementById('canvas');
    const [width,height]=[window.innerWidth,window.innerHeight];
    canvas.width=width;
    canvas.height=height;
    //画笔
    const  ctx=canvas.getContext('2d');

    //小球对象化
    class Ball{
        constructor(r,color='#000'){
            this.color=color;
            this.r=r;
            this.x=0;
            this.y=0;
        }
        draw(ctx){
            ctx.save();
            ctx.beginPath();
            ctx.fillStyle=this.color;
            ctx.arc(this.x,this.y,this.r,0,Math.PI*2);
            ctx.fill();
            ctx.restore();
        }
    }

    //实例化小球
    let ball=new Ball(15);
    ball.y=50;
    ball.x=width/2;

    //记录时间 time
    let time=new Date();

    //重力 gravity
    const gravity=0.01;

    //弹力
    const bounding=-0.8;

    //速度/毫秒 vy
    let vy=0.3;
    let vx=0.3;

    //动画方法
    function animate(){
        //时间差
        let now=new Date();
        let diff=now-time;
        time=now;

        //加速度
        vy+=gravity;

        //设置小球位置
        ball.y+=vy*diff;
        ball.x+=vx*diff;

        //底部碰撞
        if(ball.y+ball.r>height){
            ball.y=height-ball.r;
            vy*=bounding;
        }
        //左侧碰撞
        if(ball.x-ball.r<0){
            ball.x=ball.r;
            vx*=bounding;
        }
        //右侧碰撞
        if(ball.x+ball.r>width){
            ball.x=width-ball.r;
            vx*=bounding;
        }

    }

    //渲染方法
    !(function render(){
        //设置动画
        animate();
        //擦除
        ctx.clearRect(0,0,width,height);
        //绘图
        ball.draw(ctx);
        //递归调用render 函数
        window.requestAnimationFrame(render);
    })()
</script>

小球弹性运动.gif

补间动画

颜色渐变

<script src="./js/d3-color.js"></script>
<script src="./js/Tween.js"></script>
<script>
    const [width,height]=[window.innerWidth,window.innerHeight];
    const canvas=document.getElementById('canvas');
    canvas.width=width;
    canvas.height=height;
    const  ctx=canvas.getContext('2d');

    //小球对象
    class Ball{
        constructor(r,color='#000'){
            this.color=color;
            this.r=r;
            this.x=0;
            this.y=0;
        }
        draw(ctx){
            ctx.save();
            ctx.beginPath();
            ctx.fillStyle=this.color;
            ctx.arc(this.x,this.y,this.r,0,Math.PI*2);
            ctx.fill();
            ctx.restore();
        }
    }

    //实例化小球
    const ball=new Ball(100,'green');
    ball.x=width/2;
    ball.y=200;

    //实例化Tween 对象,为其添加目标对象
    let tween = new TWEEN.Tween(ball);
    //设置目标对象一段时间后的状态to({ key:val }, 时间长度)
    tween.to({color:'#00acec'}, 2000);
    //开始计时 start()
    tween.start();
    //重复 repeat(Infinity)
    tween.repeat(Infinity);
    //悠悠球 yoyo(true)
    tween.yoyo(true);
    //设置插值
    //Linear, Quadratic, Cubic, Quartic, Quintic, Sinusoidal, Exponential, Circular, Elastic, Back, Bounce
    tween.easing(TWEEN.Easing.Bounce.Out);
    //事件监听
    //onUpdate,onStart,onStop,onComplete
    tween.onUpdate(function(){
        //console.log(this.x)
    });


    //渲染方法
    !(function render(){
        //更新目标目标对象的状态
        TWEEN.update();

        //擦除
        ctx.clearRect(0,0,width,height);
        //绘图
        ball.draw(ctx);
        //递归调用render 函数
        window.requestAnimationFrame(render);
    })()

</script>

补间动画-变色.gif

图形的拖拽

1、矩形的拖拽

<canvas id="canvas"></canvas>
<script>
    const canvas=document.getElementById('canvas');
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    const  ctx=canvas.getContext('2d');

    //矩形对象
    class Rectangle{
        constructor(width=0,height=0,color='#000'){
            this.width=width;
            this.height=height;
            this.color=color;
            this.x=0;
            this.y=0;
        }
        draw(ctx){
            const {x,y,width,height,color}=this;
            ctx.save();
            ctx.fillStyle=color;
            ctx.fillRect(x,y,width,height);
            ctx.restore();
        }
    }

    //实例化矩形对象
    const rect = new Rectangle(200,100);
    rect.x=100;
    rect.y=100;

    //鼠标位减图形位
    let subPos=null;
    //图形是否被选择
    let selected=false;

    //点击事件
    canvas.addEventListener('mousedown',mousedownFn);
    //移动
    canvas.addEventListener('mousemove',mousemoveFn);
    //鼠标抬起
    canvas.addEventListener('mouseup',mouseupFn);

    function mousedownFn(event){
        //鼠标位置
        const mousePos=getMousePos(event);
        //鼠标位减图形位
        subPos={
            x:mousePos.x-rect.x,
            y:mousePos.y-rect.y
        }
        
        //判断鼠标是否在图形中
        selected=containPoint(rect,mousePos);
        
        console.log(selected);
    }
    function mousemoveFn(event){
        //鼠标位置
        const mousePos=getMousePos(event);
        /*如果鼠标选择了图形
        *   让图形跟着鼠标动
        *   按需渲染
        * */
        if(selected){
            rect.x=mousePos.x-subPos.x;
            rect.y=mousePos.y-subPos.y;
            render();
        }

    }
    function mouseupFn(event){
        //鼠标抬起,取消拖拽
        selected=false;
    }
    //判断点是否在图形中
    function containPoint(obj,mousePos){
        //解构图形位置和尺寸
        const {x,y,width,height}=obj;
        //解构鼠标位置
        const {x:mx,y:my}=mousePos;

        //计算鼠标和图形上、下、左、右边界的关系
        const l=mx>x;
        const r=mx<x+width;
        const t=my>y;
        const b=my<y+height;

        return l&&r&&t&&b;
    }

    //获取鼠标在canvas中的位置
    function getMousePos(event){
        //获取鼠标位置
        const {clientX,clientY}=event;
        //获取canvas 边界位置
        const {top,left}=canvas.getBoundingClientRect();
        //计算鼠标在canvas 中的位置
        const x=clientX-left;
        const y=clientY-top;
        return {x,y};
    }

    //渲染方法
    render();
    function render(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        rect.draw(ctx);
    }

</script>

2、圆形的拖拽

<canvas id="canvas"></canvas>
<script>
    const canvas=document.getElementById('canvas');
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    const  ctx=canvas.getContext('2d');

    //简单圆形
    class Ball{
        constructor(r=0,color='#000'){
            this.r=r;
            this.color=color;
            this.x=0;
            this.y=0;
        }
        draw(ctx){
            const {x,y,r,color}=this;
            ctx.save();
            ctx.beginPath();
            ctx.arc(x,y,r,0,Math.PI*2);
            ctx.fillStyle=color;
            ctx.fill();
            ctx.restore();
        }
    }

    const ball = new Ball(90);
    ball.x=200;
    ball.y=200;
    render();

    /*鼠标拖拽*/
    //鼠标位减图形位
    let subPos=null;
    //图形是否被选择
    let selected=false;

    //点击事件
    canvas.addEventListener('mousedown',mousedownFn);
    //移动
    canvas.addEventListener('mousemove',mousemoveFn);
    //鼠标抬起
    canvas.addEventListener('mouseup',mouseupFn);

    function mousedownFn(event){
        //鼠标位置
        const mousePos=getMousePos(event);
        //鼠标位减图形位
        subPos={
            x:mousePos.x-ball.x,
            y:mousePos.y-ball.y,
        };
        //设置选择状态
        selected=containPoint(ball);
    }
    function mousemoveFn(event){
        const mousePos=getMousePos(event);
        if(selected){
            ball.x=mousePos.x-subPos.x;
            ball.y=mousePos.y-subPos.y;
            render();
        }
    }
    function mouseupFn(){
        selected=false;
    }
    //判断点是否在图形中
    function containPoint({r}){
        const {x,y}=subPos;
        //获取鼠标到圆心的距离 len
        const len=Math.sqrt(x*x+y*y);
        //判断鼠标到圆心的距离是否小于r
        return len<r;
    }

    //获取鼠标在canvas中的位置
    function getMousePos(event){
        //获取鼠标位置
        const {clientX,clientY}=event;
        //获取canvas 边界位置
        const {top,left}=canvas.getBoundingClientRect();
        //计算鼠标在canvas 中的位置
        const x=clientX-left;
        const y=clientY-top;
        return {x,y};
    }

    function render(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        ball.draw(ctx);
    }

</script>

3、扇形的拖拽

<canvas id="canvas"></canvas>
<script>
    const canvas=document.getElementById('canvas');
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    const  ctx=canvas.getContext('2d');

    //扇形
    class Sector{
        constructor(r=0,startAngle=0,endAngle=Math.PI*2,color='#000'){
            this.r=r;
            this.startAngle=startAngle;
            this.endAngle=endAngle;
            this.color=color;
            this.x=0;
            this.y=0;
        }
        draw(ctx){
            const {x,y,r,startAngle,endAngle,color}=this;
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(x,y);
            ctx.arc(x,y,r,startAngle,endAngle);
            ctx.closePath();
            ctx.fillStyle=color;
            ctx.fill();
            ctx.restore();
        }
    }

    /*扇形弧度设置则:起始弧度和结束弧度都大于0*/
    const sector = new Sector(200,0,Math.PI/3);
    sector.x=200;
    sector.y=200;
    render();

    /*鼠标拖拽*/
    //鼠标位减图形位
    let subPos=null;
    //图形是否被选择
    let selected=false;

    //点击事件
    canvas.addEventListener('mousedown',mousedownFn);
    //移动
    canvas.addEventListener('mousemove',mousemoveFn);
    //鼠标抬起
    canvas.addEventListener('mouseup',mouseupFn);

    function mousedownFn(event){
        //鼠标位置
        const mousePos=getMousePos(event);
        //鼠标位减图形位
        subPos={
            x:mousePos.x-sector.x,
            y:mousePos.y-sector.y,
        };
        //设置选择状态
        selected=containPoint(sector,mousePos);
    }
    function mousemoveFn(event){
        const mousePos=getMousePos(event);
        if(selected){
            sector.x=mousePos.x-subPos.x;
            sector.y=mousePos.y-subPos.y;
            render();
        }
    }
    function mouseupFn(event){
        selected=false;
    }

    //判断点是否在图形中
    function containPoint(obj){
        const {r,startAngle,endAngle}=obj;
        //获取鼠标到圆心的距离
        const {x,y}=subPos;
        const len=Math.sqrt(x*x+y*y);

        //判断鼠标到圆心的距离是否小于半径
        const b1=len<r;

        //获取鼠标位减图形位的方向 dir
        const dir=Math.atan2(y,x);

        //判断鼠标向量的方向是否在扇形的夹角之间
        const b2=dir>startAngle&&dir<endAngle;


        return b1&&b2;
    }

    //获取鼠标位置
    function getMousePos(event){
        //获取鼠标位置
        const {clientX,clientY}=event;
        //获取canvas 边界位置
        const {top,left}=canvas.getBoundingClientRect();
        //计算鼠标在canvas 中的位置
        const x=clientX-left;
        const y=clientY-top;
        return {x,y};
    }


    function render(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        sector.draw(ctx);
    }

</script>

小结:1)鼠标移动距离的获取;2)判断鼠标是否在图形中;

合成

1、刮刮乐

背景图资源

ggl-back.png

刮刮乐.gif

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,user-scalable=no">
    <title>刮刮乐</title>
    <style>
        body{margin: 0;overflow: hidden}
        #canvas{
            margin: 100px;
            background-image: url("./images/ggl-back.png");
        }
    </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    const canvas=document.getElementById('canvas');
    const ctx=canvas.getContext('2d');

    /*图像尺寸,遮罩图和背景图尺寸一样*/
    const [width,height]=[395,188];
    canvas.width=385;
    canvas.height=188;

    /*建立图像源*/
    const img=new Image();
    img.src='./images/ggl-mask.png';
    img.onload=function(){
        /*绘制遮罩层*/
        ctx.drawImage(img,0,0);
    };

    /*
    * 线对象 Line
    *   ctx 上下文对象
    *   drawing 是否正在绘图
    *
    *   鼠标按下 moveTo(x,y)
    *       记录正在绘图的状态 drawing
    *       保存状态
    *       设置全局合成属性globalCompositeOperation 为destination-out
    *       线宽lineWidth为30
    *       moveTo()设置路径起点
    *   鼠标移动 lineTo(x,y)
    *       lineTo()绘制下一个点
    *       stroke()描边
    *   鼠标抬起 restore()
    *       状态还原
    *       取消正在绘图的状态
    * */
    class Line{
        constructor(ctx){
            this.ctx=ctx;
            this.drawing=false;
        }
        moveTo(x,y){
            const {ctx}=this;
            this.drawing=true;
            ctx.save();
            ctx.globalCompositeOperation='destination-out';
            ctx.lineWidth=20;
            ctx.moveTo(x,y);
        }
        lineTo(x,y){
            const {ctx}=this;
            ctx.lineTo(x,y);
            ctx.stroke();
        }
        restore(){
            this.ctx.restore();
            this.drawing=false;
        }
    }


    /*实例化线对象 Line*/
    const line=new Line(ctx);


    /*==========鼠标事件===========*/
    /*鼠标按下*/
    canvas.addEventListener('mousedown',function(event){
        //鼠标左键按下
        if(event.buttons===1) {
            //获取鼠标位置
            const {x, y} = getMousePos(event);
            //绘制起点
            line.moveTo(x,y);
        }
    });
    /*鼠标移动*/
    canvas.addEventListener('mousemove',function(event){
        //鼠标左键按下且处于绘图状态
        if(event.buttons===1&&line.drawing) {
            //获取鼠标位置
            const {x, y} = getMousePos(event);
            //绘制下一个点
            line.lineTo(x,y);
        }
    });
    /*鼠标抬起*/
    canvas.addEventListener('mouseup',function(event){
        //鼠标左键按下
        if(event.buttons===1) {
            //状态还原
            line.restore();
        }
    });


    //获取鼠标在canvas中的位置
    function getMousePos(event){
        //获取鼠标位置
        const {clientX,clientY}=event;
        //获取canvas 边界位置
        const {top,left}=canvas.getBoundingClientRect();
        //计算鼠标在canvas 中的位置
        const x=clientX-left;
        const y=clientY-top;
        return {x,y};
    }

</script>
</body>
</html>