104

看了180分钟的视频,写了半天的代码 - 易墨

 6 years ago
source link: http://www.cnblogs.com/morang/p/7636148.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

观视频《月薪4万的程序员有多强?半小时原生JS开发打气球游戏,征服现场数万人!》

清晨,日常打开B站,被首页此视频的标题所吸引,虽一看就是标题党,但还是没能抑制住好奇心。
视频共计60*3分钟,学习到了很多东西。其中后半部分有许多正三观的见解也非常认同。
视频地址:https://www.bilibili.com/video/av15152538/
在线试玩:http://sandbox.runjs.cn/show/luderhbq
参考视频写的demo:https://coding.net/u/yimocoding/p/WeDemo/git/tree/气球大战

看了视频,自己来实现试试

花了半天的时间,人生中的第二个游戏终于完成了,想起第一次做拼图游戏也已经是3年前了~
来吧,试玩一下,看能消灭多少个气球【笑哭】:http://sandbox.runjs.cn/show/luderhbq
然后,一起来一步步构建自己的【气球大战】(文中代码为核心代码,后续有优化,故非完整代码),可以在runjs中去查看

1.用css3画一个气球

看视频的时候觉得自己这个会那个也会,写代码的时候才发现没有智能提示啥都不会,打错单词的次数不是一次两次~

气球效果预览

图片

css代码

//html→→_→→<div class="balloon"></div>

body{margin:0;padding:0}
.balloon{width:150px;height:150px;position:absolute;left:0;top:0;background-color:pink;border-radius:50% 50% 10% 50%;transform:rotate(45deg);box-shadow:1px 1px 20px 20px red inset}
.balloon:after{width:20px;height:20px;content:"";display:block;background:0 0;position:absolute;right:-15px;bottom:-15px;border-left:5px solid red;border-top:5px solid red}
.balloon:before{width:2px;height:50px;content:"";display:block;background:pink;position:absolute;right:-10px;top:100%;margin-top:-16px;transform:rotate(-45deg)}

2.随机创建气球

首先定义了一些变量

    var bnElements=[];//存放所有气球
    var random=Math.random;//随机函数
    var wW=window.innerWidth;//窗口宽度
    var wH=window.innerHeight;//窗口高度
    var ballW=160;//气球的宽度
    var ballH=300;//气球的宽度
    var minSpeed=3;//最小速度,每次向上移动至少3px
    var speedNum=8;//速度的定量
    var defBnNumber=10;//初始化气球

首先编写并调用初始化方法生成气球

生成气球代码

    init(defBnNumber);
    //初始化气球
    function init(num){
        //创建一个虚拟文档节点
        var docFragment=document.createDocumentFragment();
        for(var i=0;i<num;i++){
            var bnElement=document.createElement('div');
            bnElement.className='balloon';
            //速度随机,限定最小值
            var speed=Math.max(minSpeed,~~(random()*speedNum));
            bnElement.setAttribute('speed',speed);//~~取整 移动速度
            bnElement.setAttribute('id','ball-'+(bnElements.length+i+1));
            //分散排列
            var x=(~~(random()*wW))-ballW;
            x=Math.max(0,x);
            bnElement.style.left=x+'px';				
            bnElement.style.top=wH+'px';//露一点出来			
            
            //1.先将创建的气球放入创建的虚拟文档节点
            docFragment.appendChild(bnElement);
            bnElements.push(bnElement);
        }
        //2.将虚拟文档节点添加到body中
        document.body.appendChild(docFragment);
    }

图片

3.气球向上移动

创建一个move方法并在初始化后调用

气球移动代码

    move();//移动气球 只需要调用一次即可
   function move(){
    var bl=bnElements.length
    for(var i=0;i<bl;i++){
            var currentElement=bnElements[i]
            if(currentElement==null){
                continue;
            }
            var offsetTop=currentElement.offsetTop;
        if(offsetTop>-ballH){//窗口中
                var speed=currentElement.getAttribute('speed');
                currentElement.style.top=offsetTop-speed+'px'
            }
            else{
                //移除dom节点
                document.body.removeChild(currentElement);
                //移除数组中
                bnElements.splice(i,1);
                init(1);
            }
    }
    setTimeout(move,1000/30);
}

图片

4.点击气球,气球消失

发现颜色有点丑~~遂改。

气球消失代码

bindClick();
//绑定点击气球事件
function bindClick(){
    document.body.addEventListener('click',function(e){
        if(e.target.className=='balloon'){
            boom.call(e.target,function(){
                e.target.parentNode.removeChild(e.target);
                bnElements.splice(bnElements.lastIndexOf(e.target),1);
                init(1);
            });
        }
    });
}
function boom(callback){
    //var that=this; //替换了上下文,但是没有使用this的意义.
    var speed=this.getAttribute('speed');
    this.timer=setInterval(function(){
        this.style.opacity=0.1*(speed--)
        console.log(this.offsetWidth);
        if(speed<1){
            callback&&callback();
            clearInterval(this.timer);
        }
    }.bind(this),1000/30);
}

图片
核心代码终于写完,在我的纯静态工具站点生成二维码扫一扫,在我的小米手机上玩了玩,ok正常,然后再新入手的ipad中试了试。。。擦。坑爹呢,点了咋没反应啊。
好吧,为了ipad能玩,强忍着泪水(饿的)解决了iOS的safari兼容问题~

5.解决遇到的safari浏览器兼容问题

  • 问题一:Safari中单击事件不能绑定到document.body上~~,因为无效~
    解决方法:给元素加了个父级,若click事件有问题则还需要将click换成touchend

  • 问题二:transform变换z-index层级渲染异常
    解决方法:未变换的元素上添加样式:transform: translateZ(120px);
    参考文章:http://www.zhangxinxu.com/wordpress/2016/08/safari-3d-transform-z-index/

get了几个以前不知道没用过的新技能

  • 文档片段
    当需要将一堆节点添加到dom中可以使用document.createDocumentFragment();创建虚拟文档节点,让后将节点先添加到此虚拟节点中,再将此节点追加到指定元素,能够降低dom渲染次数

  • 使用位运算符取整
    取0-9的随机数 ~~(Math.random()*10) //Math.random()大于等于 0.0 且小于 1.0

  • Math.max() Math.min() 可以用来限定边界值

  • setInterval问题:

    • 可能会丢帧(浏览器的刷新频率为60FPS,一秒最大可以重绘60次),故理论上setinterval()间隔时间大于1000/60就不会参数丢帧的情况
    • 时间线偏移(甚至重叠没执行完就执行下一次任务了),若需要每次都执行完才执行下次任务则使用setTimeout+递归
    • this的传递(可以使用bind()去绑定this,不能使用call,会提示没有权限)
      传递this到setInterval中:setInterval(function(){}.bind(this),1000/30)
  • 值的相等判断使用===会比==性能好一点,大部分情况应当使用===

  • 判断回调函数并执行回调函数
    以前我是这样写的:if(typeof(callback)==='function')callback();
    视频中有用短路运算符实现即:callback&&callback()

  • 踩了踩safari的坑

  • 最可怕的事情,不是别人比你强,而是比你强的人比你还努力!!!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK