今日学习总结

前言

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

一个动画效果

飞驰的小球

梳理思路

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速查表

原文地址:https://www.cnblogs.com/wangxi01/p/12846249.html