• 今日学习总结


    前言

    发现每天只是学习,而不记录的话,基本都看完就忘记了,所以决定将看过的东西粗略记录一下。

    一个动画效果

    飞驰的小球

    梳理思路

    1、鼠标按下时,记录小球的初始位置信息
    2、鼠标按下后滑动,记录松开鼠标瞬间的移动速度
    3、鼠标松开后,在水平方向上,让小球根据刚刚记录的移动速度进行匀减速运动,竖直方向设定一个竖直向下的加速度,开始运动。
    4、水平方向速度减为0时,水平方向运动停止;竖直方向速度减为0或者足够小时,竖直方向运动停止。

    难点分析

    怎么拿到松开手瞬间的小球移动速度?
    浏览器本身就是存在反应时间的,你可以把它当做一个摄像机,在给DOM元素绑定了事件之后,每隔一段时间(一般非常的短,根据不同浏览器厂商和电脑性能而定,这里我用到chrome,保守估计为20ms)会给这个元素拍张照,记录它的状态。在按下鼠标之后的拖动过程中,事实上会给元素拍摄无数张照片。如果现在每经过一段时间,我记录当下当前照片与上一段照片的位置差,那么最后一次拍照和倒数第二次拍照的小球位置差距,是不是就可以作为离开的瞬时速度呢?
    距离图示

    初步实现

    //html
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>狂奔的小球</title>
        <link rel="stylesheet" href="css/reset.min.css">
        <style>
            html, body {
                height: 100%;
                overflow: hidden;
            }
            #box{
                position: absolute;
                top: 100px;
                left: 100px;
                 150px;
                height: 150px;
                border-radius: 50%;
                background: lightcoral;
                cursor: move;
                z-index: 0;
            }
        </style>
    </head>
    <body>
        <div id="box"></div>
    </body>
    </html>
    
    //drag.js
    class Drag {
        //ele为传入的DOM对象
        constructor(ele) {
                //初始化参数
            this.ele = ele;
            ['strX', 'strY', 'strL', 'strT', 'curL', 'curT'].forEach(item => {
                this[item] = null;
            });
            //为按下鼠标绑定事件,事件函数一定要绑定this,在封装过程中this统一指定为实例对象,下不赘述
            this.DOWN = this.down.bind(this);
            this.ele.addEventListener('mousedown', this.DOWN);
        }
        down(ev) {
            let ele = this.ele;
            this.strX = ev.clientX;//鼠标点击处到浏览器窗口最左边的距离
            this.strY = ev.clientY;//鼠标点击处到浏览器窗口最上边的距离
            this.strL = ele.offsetLeft;//元素到浏览器窗口最左边的距离
            this.strT = ele.offsetTop;//元素到浏览器窗口最上边的距离
        
            this.MOVE = this.move.bind(this);
            this.UP = this.up.bind(this);
            document.addEventListener('mousemove', this.MOVE);
            document.addEventListener('mouseup', this.UP);
            
            //flag
            //清理上一次点击形成的一些定时器和变量
            clearInterval(this.flyTimer);
            this.speedFly = undefined;
            clearInterval(this.dropTimer);
        }
        move(ev) {
            let ele = this.ele;
            this.curL = ev.clientX - this.strX + this.strL;
            this.curT = ev.clientY - this.strY + this.strT;
            ele.style.left = this.curL + 'px';
            ele.style.top = this.curT + 'px';
            
            //flag
            //功能: 记录松手瞬间小球的速度
            if (!this.lastFly) {
                this.lastFly = ele.offsetLeft;
                this.speedFly = 0;
                return;
            }
            this.speedFly = ele.offsetLeft - this.lastFly;
            this.lastFly = ele.offsetLeft;
        }
        up(ev) {
            //给前两个事件解绑
            document.removeEventListener('mousemove', this.MOVE);
            document.removeEventListener('mouseup', this.UP);
            
            //flag
            //水平方向
            this.horizen.call(this);
            this.vertical.call(this);
        }
        //水平方向的运动
        horizen() {
            let minL = 0,
                maxL = document.documentElement.clientWidth - this.ele.offsetWidth;
            let speed = this.speedFly;
            speed = Math.abs(speed);
            this.flyTimer = setInterval(() => {
                speed *= .98;
                Math.abs(speed) <= 0.1 ? clearInterval(this.flyTimer):null;
                //小球当前到视口最左端的距离
                let curT = this.ele.offsetLeft;
                curT += speed;
                //小球到达视口最右端,反弹
                if (curT >= maxL) {
                    this.ele.style.left = maxL + 'px';
                    speed *= -1;
                    return;
                }
                //小球到达视口最右端,反弹
                if (curT <= minL) {
                    this.ele.style.left = minL + 'px';
                    speed *= -1;
                    return;
                }
                this.ele.style.left = curT + 'px';
            }, 20);
        }
        //竖直方向的运动
        vertical() {
            let speed = 9.8,
                minT = 0,
                maxT = document.documentElement.clientHeight - this.ele.offsetHeight,
                flag = 0;
            this.dropTimer = setInterval(() => {
                speed += 10;
                speed *= .98;
                Math.abs(speed) <= 0.1 ? clearInterval(this.dropTimer):null
                //小球当前到视口最左端的距离
                let curT = this.ele.offsetTop;
                curT += speed;
                //小球飞到视口顶部,反弹
                if (curT >= maxT) {
                    this.ele.style.top = maxT + 'px';
                    speed *= -1;
                    return;
                }
                //小球落在视口底部,反弹
                if (curT <= minT) {
                    this.ele.style.top = minT + 'px';
                    speed *= -1;
                    return;
                }
                this.ele.style.top = curT + 'px';
            }, 20);
        }
    }
    window.Drag = Drag;
    

    采用发布-订阅

    我们这里的目的并不只是提供一个功能,它绝不只是一个玩具,我们应当思考,如何将它做的更有通用性,能够得到最大程度的复用。 这里,可以引用软件工程领域耳熟能详的SOLID设计原则中的O部分————开放封闭原则。

    开放封闭原则主要体现在两个方面:
    对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
    对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。

    //subscribe.js
    class Subscribe {
        constructor() {
            //创建容器
            this.pond = [];
        }
        //向容器中增加方法,注意去重
        add(fn) {
            let pond = this.pond,
                isExist = false;
            //去重环节
            pond.forEach(item => item === fn ? isExist = true : null);
            !isExist ? pond.push(fn) : null;
        }
        remove(fn) {
            let pond = this.pond;
            pond.forEach((item, index) => {
                if(item === fn) {
                    //提一下我在这里遇到的坑,这里如果写item=null是无效的
                    //例子:let a = {name: funtion(){}};
                    //let b = a.name;
                    //这个时候操作b的值对于a的name属性是没有影响的
                    pond[index] = null;
                }
            })
        }
        fire(...arg) {
            let pond = this.pond;
            for(let i = 0; i < pond.length; i++) {
                let item = pond[i];
                //如果itme为空了,最好把它删除掉
                if (item === null) {
                    pond.splice(i, 1);
                    //如果用了splice要防止数组塌陷问题,即删除了一个元素后,后面所有元素的索引默认都会减1
                    i--;
                    continue;
                }
                item(...arg);
            }
        }
    }
    window.Subscribe = Subscribe;
    
    //测试一下
    let subscribe = new Subscribe();
    let fn1 = function fn1(x, y) {
        console.log(1, x, y);
    };
    let fn2 = function fn2() {
        console.log(2);
    };
    let fn3 = function fn3() {
        console.log(3);
        subscribe.remove(fn1);
        subscribe.remove(fn2);
    };
    let fn4 = function fn4() {
        console.log(4);
    };
    
    subscribe.add(fn1);
    subscribe.add(fn1);
    subscribe.add(fn2);
    subscribe.add(fn1);
    subscribe.add(fn3);
    subscribe.add(fn4);
    setInterval(() => {
        subscribe.fire(100, 200);
    }, 1000);
    

    优化代码

    //Drag.js
    if (typeof Subscribe === 'undefined') {
        throw new ReferenceError('没有引入subscribe.js!');
    }
    
    class Drag {
        constructor(ele) {
            this.ele = ele;
            ['strX', 'strY', 'strL', 'strT', 'curL', 'curT'].forEach(item => {
                this[item] = null;
            });
            
            this.subDown = new Subscribe;
            this.subMove = new Subscribe;
            this.subUp = new Subscribe;
    
            //=>DRAG-START
            this.DOWN = this.down.bind(this);
            this.ele.addEventListener('mousedown', this.DOWN);
        }
    
        down(ev) {
            let ele = this.ele;
            this.strX = ev.clientX;
            this.strY = ev.clientY;
            this.strL = ele.offsetLeft;
            this.strT = ele.offsetTop;
    
            this.MOVE = this.move.bind(this);
            this.UP = this.up.bind(this);
            document.addEventListener('mousemove', this.MOVE);
            document.addEventListener('mouseup', this.UP);
    
            this.subDown.fire(ele, ev);
        }
    
        move(ev) {
            let ele = this.ele;
            this.curL = ev.clientX - this.strX + this.strL;
            this.curT = ev.clientY - this.strY + this.strT;
            ele.style.left = this.curL + 'px';
            ele.style.top = this.curT + 'px';
    
            this.subMove.fire(ele, ev);
        }
    
        up(ev) {
            document.removeEventListener('mousemove', this.MOVE);
            document.removeEventListener('mouseup', this.UP);
    
            this.subUp.fire(this.ele, ev);
        }
    }
    
    window.Drag = Drag;
    
    //dragExtend.js
    function extendDrag(drag) {
        //鼠标按下
        let stopAnimate = function stopAnimate(curEle) {
            clearInterval(curEle.flyTimer);
            curEle.speedFly = undefined;
            clearInterval(curEle.dropTimer);
        };
        //鼠标移动
        let computedFly = function computedFly(curEle) {
            if (!curEle.lastFly) {
                curEle.lastFly = curEle.offsetLeft;
                curEle.speedFly = 0;
                return;
            }
            curEle.speedFly = curEle.offsetLeft - curEle.lastFly;
            curEle.lastFly = curEle.offsetLeft;
        };
        //水平方向的运动
        let animateFly = function animateFly(curEle) {
            let minL = 0,
                maxL = document.documentElement.clientWidth - curEle.offsetWidth,
                speed = curEle.speedFly;
            curEle.flyTimer = setInterval(() => {
                speed *= .98;
                Math.abs(speed) <= 0.1 ? clearInterval(animateFly):null;
                let curT = curEle.offsetLeft;
                curT += speed;
                if (curT >= maxL) {
                    curEle.style.left = maxL + 'px';
                    speed *= -1;
                    return;
                }
                if (curT <= minL) {
                    curEle.style.left = minL + 'px';
                    speed *= -1;
                    return;
                }
                curEle.style.left = curT + 'px';
            }, 20);
        };
        //竖直方向的运动
        let animateDrop = function animateDrop(curEle) {
            let speed = 9.8,
                minT = 0,
                maxT = document.documentElement.clientHeight - curEle.offsetHeight;
            curEle.dropTimer = setInterval(() => {
                speed += 10;
                speed *= .98;
                Math.abs(speed) <= 0.1 ? clearInterval(animateFly):null;
                let curT = curEle.offsetTop;
                curT += speed;
                if (curT >= maxT) {
                    curEle.style.top = maxT + 'px';
                    speed *= -1;
                    return;
                }
                if (curT <= minT) {
                    curEle.style.top = minT + 'px';
                    speed *= -1;
                    return;
                }
                curEle.style.top = curT + 'px';
            }, 20);
        };
        drag.subDown.add(stopAnimate);
        drag.subMove.add(computedFly);
        drag.subUp.add(animateFly);
        drag.subUp.add(animateDrop);
    };
    

    使用:

    <script>
        //原生JS 小技巧:
        //直接写box跟document.getElementById('box')是一样的效果
        let drag = new Drag(box);
        extendDrag(drag);
    </script>
    

    看大佬们的代码总会自惭形秽,自己忙碌于业务代码多年却毫无进展,大佬们代码的思想值得学习。

    思考

    无论你是做哪个端的开发工作,其实大部分业务场景、大部分流行的框架技术都很可能会在若干年后随风而逝,但真正留下来的、伴随你一生的东西是编程思想。在我的理解中,编程的意义远不止造轮子,写插件,来显得自己金玉其外,而是留心思考,提炼出一些思考问题的方式,从而在某个确定的时间点让你拥有极其敏锐的判断,来指导和优化你下一步的决策,而不是纵身于飞速迭代的技术浪潮,日渐焦虑。我觉得这是一个程序员应该追求的东西。

    数组扁平化(flat)方法总结

    let ary = [1, [2, [3, [4, 5]]], 6];
    let str = JSON.stringify(ary);
    
    //第一种处理
    str = str.replace(/([|]))/g, '');
    str = '[' + str + ']';
    ary = JSON.parse(str);
    
    //第二种处理:扩展运算符
    while (ary.some(Array.isArray())) {
      ary = [].concat(...ary);
    }
    
    //第三种处理:递归处理
    let result = [];
    let fn = function(ary) {
      for(let i = 0; i < ary.length; i++) }{
        let item = ary[i];
        if (Array.isArray(ary[i])){
          fn(item);
        } else {
          result.push(item);
        }
      }
    }
    
    //第四种处理:用 reduce 实现数组的flat方法
    function flatten(ary) {
        return ary.reduce((pre, cur) => {
            return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
        }, []);
    }
    let ary = [1, 2, [3, 4], [5, [6, 7]]]
    console.log(flatten(ary))
    

    实现一个Webpack

    看了一部分,没看懂,等详细学习后再行记录

    手写jq和axios部分ajax实现

    代码写的很好,我还没完全消化,后续记录

    实现一个符合规范的promise

    地铁上看了一半,等手操一遍理解后再记录

    Array()与Array.of()的区别

    Array.of()方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型

    let arr = Array(4)
    console.log(arr) // [empty × 4]
    console.log(arr.length) // 4
    
    let arr = Array.of(4)
    console.log(arr) // [4]
    console.log(arr.length) // 1
    

    一个图片展示方案

    利用img的三个原生方法:onloadonerroronabort,在不同情况下做不同的展示,从而达到让用户感到体验非常棒的呈现方法。
    图片展示方案代码

    Markdown速查表

    Markdown速查表

  • 相关阅读:
    ESFramework Demo -- 动态组及群聊Demo(附源码)
    反射整理学习
    JavaScript 每周导读
    SQLSERVER 中的 with锁级别
    代码细节重构:请对我的代码指手划脚
    SQLServer查询死锁语句
    模块加载系统 v16
    数据结构之排序算法C#实现
    浅谈操作系统对内存的管理
    如何编写可维护的面向对象JavaScript代码
  • 原文地址:https://www.cnblogs.com/wangxi01/p/12846249.html
Copyright © 2020-2023  润新知