简单实现背景图片碎片动画

某天,机缘巧合之下看到一文章,然后被深深的迷上了,链接在此brucelyy:用又学了一招,活变美女(但是已经404了)
然后花几天时间去封装一下,水平有限,所以标题注明简!陋!!版!!!
首先我们用的女神图片

一,基本框架搭建

先来看看基本框架构建长这样子

<!DOCTYPE HTML>
<html>

    <head>
        <title>我家女神</title>
        <style>
            html {
                overflow: hidden;
            }

            #wrapper {
                width: 40rem;
                height: 25rem;
                position: relative;
                margin: 20px auto;
            }

            #wrapper div {
                background-image: url("http://b-ssl.duitang.com/uploads/item/201801/14/20180114134621_auNRE.jpeg");
                background-repeat: no-repeat;
                background-size: 40rem auto;
                position: absolute;
                width: 40rem;
                height: 40rem;
            }
        </style>
    </head>

    <body>
        <button id="button" type="button">点击看美女</button>
        <div id="wrapper">
            <div></div>
        </div>
    </body>

</html>

现在来逐步分析一下主要代码作用:
html {overflow: hidden;} //隐藏滚动条引起的抖动
其中能实现的关键布局在于下面代码:

#wrapper div {
    background-image: url("http://b-ssl.duitang.com/uploads/item/201801/14/20180114134621_auNRE.jpeg");
    background-repeat: no-repeat;
    background-size: 40rem auto;
    position: absolute;
    width: 40rem;
    height: 40rem;
}

如果不使用绝对定位接下来的碎片化无法完成的,
由此也可以发现简陋版目前问题一:必须使用背景图实现;
背景图引起的问题二:需要设置高宽尺寸
其他就没什么好说了,目前html+部分就出来了

二,Javascript实现

其实原理说起来也不复杂,就是生成多个小元素分别定位截图,然后通过伸缩,旋转,变形,透明等效果用时间差展现.这里直接使用3属性,控制一个类匹配控制效果
生成时间差的:

// 生成随机时间差
function Random(start, end) {
  return Math.random() * (end - start) + start;
}

实现效果是通过拆分多个小碎片然后记录当前位置定位图片位置,做成类似雪碧图的效果

// 拆分多个小碎片然后记录当前位置定位图片位置,做成类似雪碧图的效果
for (var i = 0; i < r; i++) {
  for (var j = 0; j < c; j++) {
    var _div = document.createElement('div');
    var _left = j * w, _top = i * h;
    _div.style.Text = 'width:' + w + 'px;height:' + h + 'px;left:' + _left + 'px;top:' + _top + 'px; opacity:0;background-position:' + (-_left) + 'px ' + (-_top) + 'px';
    _div.style.transition = 'all ' + Random(1, 1.8) + 's  ease';
    _div.style.transform = 'perspective(800px) translate3d(' + Random(-200, 200) + 'px, ' + Random(-200, 200) + 'px,300px) rotate(' + Random(-90, 90) + 'deg) scale(' + Random(0, 2) + ')'
    box.appendChild(_div);
  }
}

因为这样子分别插入太耗性能了,我是通过字符创批量插入
由此也可以发现简陋版目前问题三:性能损耗大,暂时思路可以考虑canvas,不过我研究不多,就先用字符串解决先;

触发的关键样式:

#div.set div {
    opacity: 1 !important;
    transform: perspective(800px) translate3d(0px, 0px, 0px) rotate(0deg) scale(1) !important;
    -moz-transform: perspective(800px) translate3d(0px, 0px, 0px) rotate(0deg) scale(1) !important;
    -webkit-transform: perspective(800px) translate3d(0px, 0px, 0px) rotate(0deg) scale(1) !important;
}

这个好在是通过样式控制而不是js触发,省事好多

三,终于开始封装了

这里我是基于提升个人写作水平跟对新手友好态度写得比较详细啰嗦,老手直接跳到最下看代码就行了.
现在大家应该都明白原理了,还是为了省事,我就直接用jQuery封装了,常规步骤先写个外层:

$(function($) {
    //默认配置
    var defaults = {
    };
}(jQuery));

然后前面的时间差函数可以直接在jQuery内部作为全局函数的插件附加到内核上去的,而常规的配置可以有:触发元素,分列数量,动画时间,延迟时间和控制动画的类名;即

$(function ($) {

  //默认配置
  var defaults = {
    className: 'explode',//控制类名
    button: null, //触发按键
    lines: 5, //分割行
    rows: 5, //分割列
    delay: 0, //ms,动画自动执行时间,默认300毫秒,0为不触发
    time: [1, 1.8], //动画时间范围
  };

  $.fn.explode = function (options) {

    //保持this指向,此处没有必要将this包在$号中如$(this),因为this已经是一个jQuery对象。
    var $self = this,
      _explode = {

        //初始化
        init: function () {

          //执行条件
          options = $.extend(defaults, options);

        },

      }

    // 生成随机时间差
    $.Random = function (start, end) {
      return Math.random() * (end - start) + start;
    }
  }(jQuery))

里面有些值得一说的代码片段: var $self = this,以前我以为应该是var $self = $(this),其实这里的this是指向触发对象,例如$(‘#div’).explode(),
所以这里的this === $(‘#div’)?
其实不是,就算内容一样,但其实指向的堆栈不一样,所以不相等.详情可参考我之前写的笔记关于基本类型和引用类型小知识

这是可配置项,如果不懂用法可以百度一下,总的来说就合并成一个对象,有相同配置后面覆盖前面的;
options = $.extend(defaults, options);

原文说的批量插入dom元素修改如下:

// 生成DOM
createDom: function() {
  var i = 0,
    j = 0,
    width = $self.width(),
    height = $self.height(),
    w = width / options.lines,
    h = height / options.rows,
    str = '';

  // 切割碎片
  for (; i < options.rows; i++) {
    for (j = 0; j < options.lines; j++) {
      //填充截图动画
      var _left = j * w,
        _top = i * h;

      str += "<div style='width:" + w + "px;\
                        height:" + h + "px;\
                        left:" + _left + "px;\
                        top:" + _top + "px;\
                        opacity:0;\
                        background-position:" + (-_left) + "px " + (-_top) + "px;\
                        transition:all " + $.Random(options.time[0], options.time[1]) + "s ease;\
                        transform:perspective(" + width + "px) translate3d(" + $.Random(-w, w) + "px, " + $.Random(-w, w) + "px,300px) rotate(" + $.Random(-90, 90) + "deg) scale(" + $.Random(0, 2) + ")'></div>"
    }
  };

  return str;
}

因为都没什么难点,就不说了,相信大家都看得明白

考虑到插件最好能解决大多数问题,用户只要调用就完成效果,所以我把控制样式也动态加进去


//添加归为样式
addStyle: function () {
    var $style = $('style'),
        _w = $self.width(),
        _style = '#wrapper div {background-image:url(' + $self.attr('data-img') + ')}\
                #wrapper.' + options.className + ' div {\
                    opacity: 1!important;\
                    transform: perspective(' + _w + ') translate3d(0px, 0px, 0px) rotate(0deg) scale(1)!important;\
                    -moz-transform: perspective(' + _w + ') translate3d(0px, 0px, 0px) rotate(0deg) scale(1)!important;\
                    -webkit-transform: perspective(' + _w + ') translate3d(0px, 0px, 0px) rotate(0deg) scale(1)!important;\
                }';

    //已有样式区域就追加文本,否则新增
    $style.length
        ? $style.append(_style)
        : $('<style />')
            .text(_style)
            .appendTo('body');
}

还有上面提到的触发元素,就绑定跟解绑

//绑定
bind: function() {
    //如果有触发元素
    if(options.button) $(options.button).click(function() {
        $self.toggleClass(options.className);
    })
},

//移除绑定
destroy: function() {
    $(options.button).unbind('click')
},

加个自动执行跟链式调用就差不多了,init改为这样

//初始化
init: function() {

    //执行条件
    options = $.extend(defaults, options);

    //添加归位样式
    this.addStyle();

    //批量渲染DOM
    $self.append(this.createDom());

    //绑定
    this.bind();

    //如果有自动执行要求,默认300
    if(options.delay) setTimeout(function() {
        $self.addClass(options.className);
    }, options.delay)

    //链式调用
    return this;
},

调用方式如下:

$(function() {
    $('#wrapper').explode({
        className: 'explode',
        button: $('#button'),
        lines: 5,
        rows: 5,
        delay: 300,
        time: [1, 2]
    });
})

嗯,简陋版就这样了,下面是完整代码:

<!DOCTYPE HTML>
<html>

    <head>
        <title>美女</title>
        <style>
            html {
                overflow: hidden;
            }

            #wrapper {
                width: 500px;
                height: 500px;
                position: relative;
                margin: 20px auto;
            }

            #wrapper div {
                background-repeat: no-repeat;
                background-size: 40rem auto;
                position: absolute;
                width: 100px;
                height: 100px;
            }

            #button{
                position: relative;
                z-index: 10;
            }
        </style>
    </head>

    <body>
        <button id="button" type="button">点击看美女</button>
        <div id="wrapper" data-img='http://b-ssl.duitang.com/uploads/item/201801/14/20180114134621_auNRE.jpeg'></div>

        <script src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
        <script>
            $(function ($) {

                //默认配置
                var defaults = {
                    className: 'explode', //控制类名
                    button: null, //触发按键
                    lines: 5, //分割行
                    rows: 5, //分割列
                    delay: 0, //ms,动画自动执行时间,默认300毫秒,0为不触发
                    time: [
                        1, 1.8
                    ], //动画时间范围
                };

                $.fn.explode = function (options) {
                    //保持this指向,此处没有必要将this包在$号中如$(this),因为this已经是一个jQuery对象。
                    var $self = this,
                        _explode = {

                            //初始化
                            init: function () {

                                //执行条件
                                options = $.extend(defaults, options);

                                //添加归位样式
                                this.addStyle();

                                //批量渲染DOM
                                $self.append(this.createDom());

                                //绑定
                                this.bind();

                                //如果有自动执行要求,默认300
                                if (options.delay) 
                                    setTimeout(function () {
                                        $self.addClass(options.className);
                                    }, options.delay)

                                    //链式调用
                                return this;
                            },

                            //绑定
                            bind: function () {
                                //如果有触发元素
                                if (options.button) 
                                    $(options.button).click(function () {
                                        $self.toggleClass(options.className);
                                    })
                            },

                            //移除绑定
                            destroy: function () {
                                $(options.button).unbind('click')
                            },

                            //生成DOM
                            createDom: function () {
                                var i = 0,
                                    j = 0,
                                    width = $self.width(),
                                    height = $self.height(),
                                    w = width / options.lines,
                                    h = height / options.rows,
                                    str = '';

                                //切割碎片
                                for (; i < options.rows; i++) {
                                    for (j = 0; j < options.lines; j++) {
                                        //填充截图动画
                                        var _left = j * w,
                                            _top = i * h;

                                        str += "<div style='width:" + w + "px;\
                                                    height:" + h + "px;\
                                                    left:" + _left + "px;\
                                                    top:" + _top + "px;\
                                                    opacity:0;\
                                                    background-position:" + (-_left) + "px " + (-_top) + "px;\
                                                    transition:all " + $.Random(options.time[0], options.time[1]) + "s ease;\
                                                    transform:perspective(" + width + "px) translate3d(" + $.Random(-w, w) + "px, " + $.Random(-w, w) + "px,300px) rotate(" + $.Random(-90, 90) + "deg) scale(" + $.Random(0, 2) + ")'></div>"
                                    }
                                };

                                return str;
                            },

                            //添加归为样式
                            addStyle: function () {
                                var $style = $('style'),
                                    _w = $self.width(),
                                    _style = '#wrapper div {background-image:url(' + $self.attr('data-img') + ')}\
                                            #wrapper.' + options.className + ' div {\
                                                opacity: 1!important;\
                                                transform: perspective(' + _w + ') translate3d(0px, 0px, 0px) rotate(0deg) scale(1)!important;\
                                                -moz-transform: perspective(' + _w + ') translate3d(0px, 0px, 0px) rotate(0deg) scale(1)!important;\
                                                -webkit-transform: perspective(' + _w + ') translate3d(0px, 0px, 0px) rotate(0deg) scale(1)!important;\
                                            }';

                                //已有样式区域就追加文本,否则新增
                                $style.length
                                    ? $style.append(_style)
                                    : $('<style />')
                                        .text(_style)
                                        .appendTo('body');
                            }
                        }

                    //自动初始化
                    _explode.init();
                }

                //随机范围
                $.Random = function (start, end) {
                    return Math.random() * (end - start) + start;
                }
            }(jQuery));
        </script>
        <script>
            $(function () {
                $('#wrapper').explode({
                    className: 'explode',
                    button: $('#button'),
                    lines: 5,
                    rows: 5,
                    delay: 300,
                    time: [1, 2]
                });
            })
        </script>
    </body>

</html>

效果是出来的,弊端也很多
问题四:碎片没有消失,衹是隐藏,很容易会遮挡住其他元素的交互事件,例如案例中的按钮
问题五:能不能实现出在实际网站里,图片碎片进入,往下滑才会引发视图下面的图片执行,更大可能是要结合懒加载插件使用

嗯,暂时想到这么多,虽然能力不过硬,有想法还是好的,以后慢慢完善,有人可以交流下会好点

    原文作者:炸鸡超人
    原文地址: https://segmentfault.com/a/1190000019937007
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞