zoukankan      html  css  js  c++  java
  • canvas学习和面向对象(二)

    Canvas 学习(二)

    上一篇Canvas 学习(一)中我是用canvas绘制了一些基本和组合的图形.

    现在开始绘制图片和动画帧,以及面向对象的升级版本.

    还是一样,看代码,所有的代码都托管在github

    先看第一个例子,绘制一张图片

    01-绘制图片.htm

    <body>
        <canvas id="img" height="400" width="400"></canvas>
        <script src="01绘制图片.js"></script>
    </body>
    
    

    这里的图片源有以下方式

    • 在页面中添加一个image标签,然后通过document.getElementById() 的方式来拿到

    • 通过new Image().src = ...; 通过这种方法来拿到.

    在这里,我使用第二种方法,我的图片放在了img文件夹下.

    • HTMLCanvasElement

    ​可以使用另一个canvas元素作为你的图片源。

    • ImageBitmap

    这是一个高性能的位图,可以低延迟地绘制,它可以从上述的所有源以及其它几种源中生成。

    01绘制图片.js

    var ctx = document.getElementById('img').getContext('2d');
    var img = new Image();
    img.src = './img/0.jpg';
    img.onload = function () {
        ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);
    }
    

    美腻吧

    在给img对象赋值了src 属性的时候,浏览器会立即开始加载图片,只有当图片加载完毕的时候,我们才能开始绘制图片,所以使用了img.onload = functioin(){...}; 的方式

    context.drawImage()有三种方法,下面开始介绍

    • drawImage(image, x, y)

    其中 image 是 image 或者 canvas 对象,x 和 y 是其在目标 canvas 里的起始坐标。

    这里就会按照原生图片进行绘制,不会进行缩放或者裁剪

    • drawImage(image, x, y, width, height)

    这个方法多了2个参数:width 和 height,这两个参数用来控制 当像canvas画入时应该缩放的大小.

    由于我们在画图的时候,希望图片应该按照原比例来呈现.所以就想在页面中写入img标签一样,我们通常情况下只是放入一个参数,然后使用公式计算另一个参数.

    由于要求

    width/height==originWidth/originHeight

    height=width/originWidth*originHeight

    ​ 使用这个公式用宽度来计算高度就可以很按照源比例绘制, 当然如果你想把脸瘦下来,那就另说了.....

    • drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
      drawImage 方法的第三个也是最后一个变种有8个新参数,用于控制做切片显示的。

    第一个参数和其它的是相同的,都是一个图像或者另一个 canvas 的引用。其它8个参数最好是参照下面的图解,前4个是定义图像源的切片位置和大小,后4个则是定义切片的目标显示位置和大小

    这个方法主要是用来做切图的,下面我们做的活动精灵刚好就用到了它.


    移动精灵

    这是我要用到的素材.

    可以看到,它就是很多很多的小图片组合在一起,那么要使它动起来的方式就很简单了.

    在画布上不停绘制每一行的小图形,当绘制一个时,就清除前面一个的痕迹.

    下面是代码

    02移动精灵.html

       <article>
            <canvas height="200" width="400" id="genius"></canvas>
            <div>
                <button id="forward">forward</button>
                <button id="right"> right</button>
                <button id="back">back</button>
                <button id="left">left</button>
            </div>
        </article>
        <script src="02移动精灵.js"></script>
    

    我放置了一个canvas画布和4个按钮,让他前后左右动.

    02移动精灵.js

    var ctx = document.getElementById('genius').getContext('2d');
    var img = new Image();
    var intervalId;
    var draw = function (direction) {
        var rowIndex = direction,  // 当前是第几行的图片
            columnIndex = 0, // 当前是第几列的图片
            frame = 6,  // 一秒有几帧
            singleWidth = 40,  // 每一个小图片的宽度
            singleHeight = 65;  // 每一个小图片的高度
        window.clearInterval(intervalId);
        intervalId = setInterval(function () {
            // 在没绘制一张小图片之前,都要清空之前绘制的图片,这样才能显示出动画效果来,
            ctx.clearRect(10, 10, singleWidth, singleHeight);
            //  在大图上剪切绘制绘制
            ctx.drawImage(img, columnIndex * singleWidth, rowIndex * singleHeight, singleWidth, singleHeight, 10, 10, singleWidth, singleHeight);
            columnIndex++;
            columnIndex %= 4;
        }, 1000 / frame);
    
    }
    

    这里draw函数里防止了主要的代码,通过计时器来使图片动起来(注意不要使用循环)

    注意在每一次移动方向后都要清除计时器,

    columnIndex %= 4;columnIndex++; 这两句是常用的循环控制语句.

    02移动精灵.js

    onload = function () {
        img.src = "./img/DMMban.png";
        img.onload = function () {
            // 用数字表示这个精灵移动的方向.
            // forward: 0,left:1,right:2,back:3
            draw(0);
        }
        document.querySelector('#forward').addEventListener('click', () => {
            draw(0);
        });
        document.querySelector('#left').addEventListener('click', () => {
            draw(1);
        });
        document.querySelector('#right').addEventListener('click', () => {
            draw(2);
        });
        document.querySelector('#back').addEventListener('click', () => {
            draw(3);
        });
    };
    
    

    下面是效果图.

    这个精灵并不是很好看,不过因为我不是做动画的,找不到比较漂亮的素材,将就看吧...


    面向对象版本

    好了,这个列子相对于第一篇文章例子要复杂一点,所以我也做了一个面向对象的版本,使用到了原型继承和一些其他的知识点.(关于原型继承和JavaScript面向对象在这里有介绍: JavaScript面向对象高级(一)

    注意封装对象的方式,我个人认为这样封装对象时非常好的. 这也是很多大牛推荐的.在我的JavaScript高级框架设计部分将会介绍jQuery对象封装的方式.

    01-移动精灵-面向对象版本.htm

    <body>
        <article>
            <canvas height="200" width="400" id="genius"></canvas>
            <div>
                <button id="forward">forward</button>
                <button id="right"> right</button>
                <button id="back">back</button>
                <button id="left">left</button>
            </div>
        </article>
        <script src="01-移动精灵-面向对象版本.js"></script>
    </body>
    
    

    01-移动精灵-面向对象版本.js

    • Genius 类的封装
    var Genius = function (option) {
        Genius.prototype._init_(option);
    }
    Genius.prototype = {
        constructor: Genius,
        // 把对象的初始化代码都放在这里,把它需要用到的所有变量都绑定到它的原型上.
        _init_: function (option) {
            this.img = option.img;
            this.rowIndex = option.rowIndex;
            this.columnIndex = option.columnIndex;
            this.frame = option.frame;
            this.singleWidth = option.singleWidth;
            this.singleHeight = option.singleHeight
        },
    	// 由于js面向对象的特点,获取会从父对象的prorotype里面获取,但是设置只会设置自己的
        draw: function (ctx, direction) {
            window.clearInterval(this.intervalId);
            this.rowIndex = direction;
            // 一定要设置,因为在setInterval里面,this指的就是window变量
            var that = this;
            this.intervalId = setInterval(function () {
                // 在没绘制一张小图片之前,都要清空之前绘制的图片,这样才能显示出动画效果来,
                ctx.clearRect(10, 10, that.singleWidth, that.singleHeight);
                //  在大图上剪切绘制绘制
                ctx.drawImage(that.img, that.columnIndex * that.singleWidth, that.rowIndex * that.singleHeight, that.singleWidth, that.singleHeight, 10, 10, that.singleWidth, that.singleHeight);
                that.columnIndex++;
                that.columnIndex %= 4;
            }, 1000 / that.frame);
    
        }
    
    }
    
    
    
    window.onload = function () {
        var ctx = document.getElementById('genius').getContext('2d');
        var oringinImg = new Image();
        oringinImg.src = "./img/DMMban.png";
        var genius;
        oringinImg.onload = function () {
        	// 实例化构造一个对象
            genius = new Genius({
                img: oringinImg,
                rowIndex: 0,
                columnIndex: 0,
                frame: 6,
                singleWidth: 40,
                singleHeight: 65
            });
            // 调用Genius的prototype里面的draw方法.
            genius.draw(ctx, 0);
    
        }
        document.querySelector('#forward').addEventListener('click', () => {
            genius.draw(ctx, 0);
        });
        document.querySelector('#left').addEventListener('click', () => {
            genius.draw(ctx, 1);
        });
        document.querySelector('#right').addEventListener('click', () => {
            genius.draw(ctx, 2);
        });
        document.querySelector('#back').addEventListener('click', () => {
            genius.draw(ctx, 3);
        });
    };
    

    这是效果图

    嗯 canvas绘制动画帧就到这里,下一篇Canvas(三) konva 将会介绍canvas的旋转和konva库.

    由于本人水平有限,如有错误,如果错误,望不吝赐教.


    最后特别感谢恩师蒋坤老师(jk)对我的知识学习和人生引导的极大帮助,非常感谢他.

    另 由于现任公司没有适合我的岗位,想求职一份,职位: 前端开发. 我的邮箱 mymeat@126.com.

  • 相关阅读:
    A1023 Have Fun with Numbers (20分)(大整数四则运算)
    A1096 Consecutive Factors (20分)(质数分解)
    A1078 Hashing (25分)(哈希表、平方探测法)
    A1015 Reversible Primes (20分)(素数判断,进制转换)
    A1081 Rational Sum (20分)
    A1088 Rational Arithmetic (20分)
    A1049 Counting Ones (30分)
    A1008 Elevator (20分)
    A1059 Prime Factors (25分)
    A1155 Heap Paths (30分)
  • 原文地址:https://www.cnblogs.com/likeFlyingFish/p/5701655.html
Copyright © 2011-2022 走看看