Canvas照片切分实际效果的完成

2021-02-22 18:37 jianzhan

以前在逛cssdesignawards时发现了1个把照片內容切分的实际效果(网站地址:https://weareludwig.com),大伙儿能够点进去看看,觉得挺酷炫的,因而自身试着完成了1下,实际效果还非常好。实际效果查询https://codepen.io/geeknoble/pen/OQaOVG

剖析

最先大家能够发现照片的內容被分为了1个个小矩形框,并对每一个矩形框开展了任意平移。Canvas的drawImage涵数能够对照片內容开展剪裁并绘图到Canvas画布中,因此该实际效果关键完成基本原理便是应用drawImage。关键实际效果有两个,1个是照片內容的打乱和还原,1个是和下张照片的切换,这两个实际效果都可以以应用drawImage,只是挪动的间距不1样。整体思路有了那末便可以去下手完成1下。

原始工作中

最先大家要原始化1些自变量,例如照片的宽高,矩形框的个数,裁切的规格等,随后再测算每一个矩形框的座标,应用1个2重循环系统将矩形框座标储存在data中。每一个矩形框有个任意位移,这个位移也必须储存起来,存在randoms中。在其中x,y表明canvas画布的座标,x1,y1表明照片剪裁的座标。

init: function (context, width, height, area, img) {
            this.context = context;
            this.img = img;
            this.imgWidth = img[0].width;          //照片宽高
            this.imgHeight = img[0].height;
            this.index = 0;                       //当今照片编号
            this.width = width;                  //画布宽高
            this.height = height;
            this.area = height/12;                     //小矩形框长度
            this.countX = width / this.area;             //水平静竖直方位小矩形框个数
            this.countY = height / this.area;
            this.wx = this.imgWidth / this.countX;      //照片在小矩形框中的宽高
            this.wy = this.imgHeight / this.countY;
            this.state = true;                   //照片情况,true表明未拆分
            this.dataFlag = true;                //小矩形框座标情况,true表明未再加任意值
            this.duration = 1000;                 //动漫時间
            this.duration2 = 1500;
            this.startTime = 0;
            this.data = [];                       //小矩形框座标信息内容
            this.randoms = [];                    //部位任意值
            //原始化矩形框座标
            var x1 = 0, y1 = 0, x = 0, y = 0;
            for (var i = 0; i < this.countY; i++) {
                for (var j = 0; j < this.countX; j++) {
                    context.drawImage(this.img[this.index], x1, y1, this.wx, this.wy, x, y, this.area, this.area);
                    //存储矩形框座标
                    this.data.push({
                        x1: x1,
                        y1: y1,
                        x: x,
                        y: y
                    });
                    //加上任意值
                    this.randoms.push(random(-this.area, this.area));
                    x1 += this.wx;
                    x += this.area;

                }
                x1 = 0;
                y1 += this.wy;
                x = 0;
                y += this.area;
            }
            this.checkMargin();
        }

检验边沿

在给矩形框加上位移以前大家必须分辨1下位移后的座标是不是超出照片界线,例如在顶部的矩形框假如是y轴挪动,那末只可以向上移,分辨的标准为当今座标再加位移值是不是小于0或超过照片的宽高。假如升级后的座标小于0,那末这个任意值1定是负数,必须把任意值改成正数,假如超过照片高宽比,那末改为负数便可。因为每一个矩形框的挪动全是在1个方位上挪动,因此我这里写成偶多位挪动x轴,单数位挪动y轴。

//检验边沿
        checkMargin: function () {
            var self = this;
            this.data.forEach(function (item, index) {
                if (index % 2 == 0) {  // 下标为2的倍数时挪动x轴,不然挪动y轴
                    if ( item.x1 + self.randoms[index] < 0)
                        // 改成正数
                        self.randoms[index] = -self.randoms[index];
                    if (item.x1 + self.wx + self.randoms[index] > self.imgWidth )
                        // 改成负数
                        self.randoms[index] = -Math.abs(self.randoms[index])
                } else {
                    if (item.y1 + self.randoms[index] < 0)
                        self.randoms[index] = -self.randoms[index];
                    if (item.y1 + self.randoms[index] + self.wy > self.imgHeight)
                        self.randoms[index] = -Math.abs(self.randoms[index])
                }
            })
        }

分离出来和还原

动漫的內容的分离出来和还原便是升级矩形框座标的值,打乱內容要是将data里的座标再加任意值,而还原便是减去任意值,

//检验边沿
        checkMargin: function () {
            var self = this;
            this.data.forEach(function (item, index) {
                if (index % 2 == 0) {  // 下标为2的倍数时挪动x轴,不然挪动y轴
                    if ( item.x1 + self.randoms[index] < 0)
                        // 改成正数
                        self.randoms[index] = -self.randoms[index];
                    if (item.x1 + self.wx + self.randoms[index] > self.imgWidth )
                        // 改成负数
                        self.randoms[index] = -Math.abs(self.randoms[index])
                } else {
                    if (item.y1 + self.randoms[index] < 0)
                        self.randoms[index] = -self.randoms[index];
                    if (item.y1 + self.randoms[index] + self.wy > self.imgHeight)
                        self.randoms[index] = -Math.abs(self.randoms[index])
                }
            })
        }

在存储好座标后便可以去完成平挪动画了,挪动的全过程有1个光滑的过渡,大家可使用Tween.js的缓动优化算法,该优化算法有4个主要参数各自是当今時间,原始部位,完毕部位,动漫時间。详尽內容能够参照张鑫旭的这篇文章内容https://www.zhangxinxu.com/wordpress/2016/12/how-use-tween-js-animation-easing/ 。根据Tween.js能够算出每帧要挪动的间距,随后再应用requestAnimationFrame去升级座标。

blockAnimation: function () {
            var flag = 1;
            if (this.state) {  // 分辨是打乱照片還是复原照片
                this.update(true)
            } else {
                flag = ⑴;
                this.update(false);
            }
            var self = this;
            this.startTime = +new Date();  // 获得当今時间

            this.state = !this.state;
            (function animation() {
                var t = +new Date();
                if (t >= self.startTime + self.duration) {  // 动漫完毕标准
                    return false;
                }
                self.data.forEach(function (item, index) {
                    if (index % 2 == 0) {
                        var pos = Math.tween.Expo.easeInOut(t - self.startTime, 0, self.randoms[index] * flag, self.duration);   // 测算出每帧挪动的间距
                        self.context.drawImage(self.img[self.index], item.x1 + pos, item.y1, self.wx, self.wy, item.x, item.y, self.area, self.area);
                    } else {
                        var pos = Math.tween.Expo.easeInOut(t - self.startTime, 0, self.randoms[index] * flag, self.duration);  
                        self.context.drawImage(self.img[self.index], item.x1, item.y1 + pos, self.wx, self.wy, item.x, item.y, self.area, self.area);
                    }
                });
                requestAnimationFrame(animation);
            })();
        }

到这里就早已完成了分离出来和还原的动漫了

照片切换

接下来刚开始解决照片切换的一部分,这里跟轮播图有点像,轮播图动漫是将每一个照片部位挪动可视性对话框宽度的间距,这里也是1样,要是将座标再加照片高宽比便可以完成y轴上的切换。和轮播图不1样的是,大家这里仅有1个canvas标识,在切换时只必须更改当今图和下1张图的座标,当今图挪动间距为y1 + pos,下张图挪动间距为y1 + pos - imgHeight(为何要减imgHeight就无需说了吧)。

//竖直拖动动漫
        verticalAnimation: function (val) {
            if (!this.time2) {
                return false;
            }
            this.checkTime(2);

            var self = this;
            val ? val = 1 : val = ⑴;  //分辨上滑還是下滑
            if ((this.index + val) < 0 || (this.index + val) >= (this.img.length)) {   //分辨照片编号是不是究竟
                return false;
            }

            this.state ? this.update(true) : this.update(false);
            this.startTime = +new Date();

            (function animation() {
                var t = +new Date();
                if (t >= self.startTime + self.duration2) {
                    val === 1 ? self.index++ : self.index--;  //调剂照片次序
                    self.index < 0 ? self.index = self.img.length - 1 : self.index;
                    self.index >= self.img.length ? self.index = 0 : self.index;
                    return false;
                }
                self.data.forEach(function (item) {
                    var pos = Math.tween.Cubic.easeInOut(t - self.startTime, 0, (self.imgHeight) * val, self.duration2);
                    // 升级当今照片座标
                    self.context.drawImage(self.img[self.index], item.x1, item.y1 + pos, self.wx, self.wy, item.x, item.y, self.area, self.area);
                    // 升级下张照片座标
                    self.context.drawImage(self.img[self.index + val], item.x1, item.y1 + pos - self.imgHeight * val, self.wx, self.wy, item.x, item.y, self.area, self.area);

                });
                requestAnimationFrame(animation);
            })()
        }

x轴的切换也是同理,如今全部作用都类似进行了,详细编码能够在codepen里查询。

以上便是本文的所有內容,期待对大伙儿的学习培训有一定的协助,也期待大伙儿多多适用脚本制作之家。