原生JS实现飞机大战游戏 超详细解析 快来做一个自己玩吧

news2025/7/13 19:51:43

目录

1. 案例分析💨

2. 适配设备 💨

3. 背景滚动💨

4. hero操作💨

5. 敌机的创建与运动💨

6. 子弹的创建与运动💨

7. 碰撞检测💨

8. 统计得分💨

9. 设置开始与结束界面💨

10. 设置带本地存储功能的排行榜💨

 

我们先来看看接下来我们要做的效果:🙋🙋🙋

f5468df57eef4b24a3ebffa50a49b664.gif 

 

有需要源码和素材的同学,在文章末尾有链接。 

下面是另外两个原生JS的游戏和项目,大家可以选择阅读,都有详细解析:

原生JS实现FlappyBird游戏:原生JS实现FlappyBird游戏 超详细解析 快来做一个自己玩吧

原生JS实现本地存储记账本:我用JS做了一个记账本 [数据可本地存储] 附万字详解

1. 案例分析💨

我们先思考一下这个游戏都由哪几个部分或者说哪些功能组成呢?

  • 开始前 :一个开始游戏面板 
  • 游戏中:背景滚动  hero的操作  敌机的创建与运动  子弹的创建与运动  碰撞检测
  • 游戏结束:一个排行榜面板

 

2. 适配设备 💨

新建一个public.js文件,这个文件放一些我们公共的方法,下面我们先定义一个isPhone方法来判断是否是移动端设备

function isPhone() {
    var arr = ["iPhone","iPad","Android"];
    var is = false;
    for (var i=0; i<arr.length; i++) {
        if (navigator.userAgent.includes(arr[i])) {
            is = true;
            break;
        }
    }
    return is;
}

在isPhone方法里我们定义了一个数组arr用来存储移动端的设备名,UserAgent是HTTP请求中的用户标识,一般发送一个能够代表客户端类型的字符串,includes 方法判断数组是否包含指定的值,包含返回 true,不包含返回 false。

我们默认是PC端,如果includes返回true则代表与数组中的元素匹配,代表是移动端设备,那么我们的isPhone方法就返回true。

这个判断移动端的方法大家可以保存下来,以后很多的项目我们也用的到。 

因为我们规定移动端下背景图片要占满屏幕,所以需要一个if语句进行判断,如果isPhone返回的是true,说明当前在移动端,我们需要修改背景图片的宽高:

sw和sh是在在外面定义的全局变量,默认情况下sw=320,sh=568,因为在后面我们还会用到sw,sh,所以如果设备是移动端的话,需要对它们进行重新赋值:

if (isPhone()) {
        var bg = document.querySelector('.contain');
        sw = document.documentElement.clientWidth + 'px';
        sh = document.documentElement.clientHeight + 'px';
        bg.style.width = sw;
        bg.style.height = sh;
}

document.documentElement.clientWidth 就是当前设备的屏幕宽度,注意加符号

我们可以在chrome浏览器下模拟移动端不同设备下是否占满全屏,每次换完设备时要刷新页面: 

 1de987a682824b36a53da4cb2f9b9a11.gif

这样的话我们适配设备的效果就完成了,成功做到了可以在移动端下占满全屏,下面就开始制作我们的飞机大战游戏吧!

 

3. 背景滚动💨

游戏背景是最外层盒子 container 的背景图片,背景图片是在y轴上平铺的,所以我们通过定时器改变背景图片的y轴位置就能达到持续滚动的效果。

我们在实现各个子模块功能的时候都写到单独的文件里,下面我们创建一个背景滚动文件 bg.js ,把相关功能的实现写在这里:

// 背景滚动
var dContainer = document.getElementById("container");
var dis = 0;  //bg滚动的量
var speed = 5;  //滚动的速度
function bgMove() {
    dis += 5;
    dis = dis>sh ? 0 : dis;
    dContainer.style.backgroundPosition = `0 ${dis}px`;
}

我们在 index.html 中定义一个定时器,然后每隔30毫秒调用 bgMove 这个方法:

function start() {
    timer = setInterval(function() {
        // 2.1 背景滚动
        bgMove();
        }, 30)
}

在我们制作的这个游戏中,不论是背景移动还是待会要做的 hero的移动,敌机的移动,最后封装的函数都需要在这个定时器里调用,这样才会有我们看到的那种动画一样的效果。 

我们看一下背景移动的效果:

d22205edee59455a9d82823ed8cbb735.gif

这样背景的滚动就完成了,下面我们开始进行 hero 的操作。

 

4. hero操作💨

我们新建一个控制 hero 移动的 js 文件:hero.js

然后分为三步

1 .获取装hero飞机的盒子

2. 添加键盘事件,判断按下的状态

3. 封装移动函数

这里需要着重强调的就是第二步,我们先看一下代码:(这里37.38.39.40是阿斯克码分别代表左上右下键)

var isLeft = false;
var isTop = false;
var isRight = false;
var isBottom = false;
//键盘按下事件
window.onkeydown = function(e) {
    if (e.keyCode === 37) {
        isLeft = true;
    } else if (e.keyCode === 38) {
        isTop = true;
    } else if (e.keyCode === 39) {
        isRight = true;
    } else if (e.keyCode === 40) {
        isBottom = true;
    }  
}
//键盘抬起事件
window.onkeyup = function(e) {
    if (e.keyCode === 37) {
        isLeft = false;
    } else if (e.keyCode === 38) {
        isTop = false;
    } else if (e.keyCode === 39) {
        isRight = false;
    } else if (e.keyCode === 40) {
        isBottom = false;
    }  
}

这里每当按下键盘或者键盘抬起的时候,我们都会判断相应的状态,如果没有这一步,我们实现不了飞机向左上飞或者向右上飞,只能要么竖着上下飞,要么横着左右飞。

这里我们再看看hero移动的函数就能更好的理解了:

var dHero = document.getElementById("hero");
function heroMove() {
    var left = dHero.offsetLeft;
    var top = dHero.offsetTop;
    if (isLeft) {
        left -= 8;
        left = left<-33 ? -33 : left;
    }
    if (isTop) {
        top -= 8;
        top = top<0? 0 : top;
    }
    if (isRight) {
        left += 8;
        left = left>sw-33 ? sw-33 : left;
    }
    if (isBottom) {
        top += 8;
        top = top>sh-82 ? sh-82 : top
    }
    dHero.style.left = left + 'px';
    dHero.style.top = top + 'px';
}

当我们按下左移键时,isLeft等于true,当我们按下上移键时,isTop等于true,所以在移动函数heroMove里,前两个if都会被执行,这样就实现了向左上方飞的效果。

把 hero 的操作函数添加到定时器中:

function start() {
        timer = setInterval(function() {
            // 2.1 背景滚动
            bgMove();
            // 2.2 hero的操作  pc键盘
            heroMove();
        }, 30)
    }

下面我们来看一下效果:

f5e8dc5457f44b3fba34dbb147339a31.gif

 这样我们就实现了通过上下左右键对 hero 的操作

 

5. 敌机的创建与运动💨

在实现敌机的创建之前,因为我们要让生成的敌机实现随机分布,所以需要先写一个随机数函数,我们就在public.js里完成:

function rand(min, max) {
    return Math.round(Math.random() * (max-min) + min)
}

创建一个 enemy.js文件编写敌机的创建与运动,首先我们写一个创建敌机的函数:

var dEnemy = document.getElementById("enemy");
function createEnemy() {
    var d = document.createElement("div");
    d.className = "enemy";
    d.style.left = rand(0,sw-38) + 'px';
    d.speed = rand(3,8);
    dEnemy.appendChild(d);
}

这里我们首先获取 enemy 元素,enemy盒子是作为装载生成敌机的父盒子,类 enemy 就是给创建的 div 盒子增加了敌机的背景,因为最外层的背景盒子我们给了他一个相对定位,然后把装载敌机的盒子一个绝对定位。这样才能让敌机在背景上移动,在类 enemy 里我们定义所有生成的敌机的 top 值都是负的,让敌机从背景外向内移动。然后把创建的div盒子作为 dEnemy 的孩子添加进去。rand函数是创建的一个返回随机数的函数。第三行语句是为了让敌机生成在背景的水平方向的任意位置上,然后让生成的敌机速度也是随机的,减去38是因为我们创建的敌机的宽度是38。

接下来我们看一下敌机的运动函数:

// 敌机的创建于运动
var dEnemy = document.getElementById("enemy");//通过概率来限制敌机的创建与游戏难度
var diff = 200; //难度系数
//敌机运动
function enemyMove() {
    // 1. 敌机的创建
    if (rand(0,diff) <= 10) {
        createEnemy()
    }
    // 2. 敌机的运动
    var es = dEnemy.children;
    for (var i=0; i<es.length; i++) {
        var e = es[i];
        if (e.offsetTop > sh) {
            // 飞出了屏幕,需要删掉
            dEnemy.removeChild(e);
            i --;  //防止漏掉元素
            continue;
        }
        e.style.top = e.offsetTop + e.speed + 'px';
    }
}

在敌机创建部分我们用了一个if语句,因为我们在通过定时器调用这个函数时,大概每秒钟会调用三十次,那样的话每次调用都创建一个敌机,敌机的数量就太多了。rand(0,200) <= 10意思就是是原来二十分之一的概率,这样生成的敌机数量正好。

还有一个值得注意的点是,当敌机飞出屏幕时,我们需要把敌机这个元素删点,那为什么要i--呢?

比如我们的敌机数组有四个元素,现在判断的是第二个元素,也就是i等于1,当我们移除掉这个元素后,原来的第三个元素就到了我们移除的第二个元素的位置上来。但是因为for循环还会进行一个i++的操作,这样i就等于2了,就是数组的第三个元素。但这其实是第四个元素,因为我们已经把第二个元素删掉了,所以就漏掉了第三个元素,就需要进行一个i--操作来防止漏掉元素。

把 enemyMove 方法添加到主页定时器中:

function start() {
        timer = setInterval(function() {
            // 2.1 背景滚动
            bgMove();
            // 2.2 hero的操作  pc键盘
            heroMove();
            // 2.3 敌机的创建与运动
            enemyMove();
        }, 30)
    }

我们看一下效果:

8e46199f02124c168e8cac131046304e.gif

 

6. 子弹的创建与运动💨

我们创建一个 bullet.js 文件,子弹的创建和上一节中敌机的创建是很相似的:

function createBullet() {
    var dHero = document.getElementById("hero");
    var d = document.createElement("div");
    d.className = "bullet";
    d.style.left = dHero.offsetLeft + 33 - 3 + 'px';
    d.style.top = dHero.offsetTop + 'px';
    dBullet.appendChild(d);
}

只不过子弹的定位是跟 hero 相关的,所以子弹的 top,left值需要用到 hero 的位置,' 33 -3 '那里前面介绍过33是指 hero 飞机宽度的一半,而3就是子弹宽度的一半,这样就能保证子弹是从飞机头的那个位置发射出来的。 

接下来我们再完成子弹的运动函数:

//子弹运动及创建
var dBullet = document.getElementById("bullet");
// 使用间隔
var space = 7;
var count = 0; //计数
//子弹运动
function bulletMove() {
    count ++;
    // 1. 子弹的创建
    if (count === space) {
        createBullet();
        count = 0;
    }
    // 2. 子弹的运动
    var bs = dBullet.children;
    for (var i=0; i<bs.length; i++) {
        var top = bs[i].offsetTop;
        if (top <= -14) {
            dBullet.removeChild(bs[i]);
            i-- ;
            continue;
        }
        bs[i].style.top = top - 9 + 'px';
    }
}

在子弹的移动函数中我们调用子弹的创建函数,通过 space 和 count 两个变量来控制子弹的生成频率,要不然子弹每隔30毫秒就生成一个就太快了。然后我们让子弹在超出边界后就自动销毁。

我们把这个方法和之前一样加到主页的定时器中:

    function start() {
        timer = setInterval(function() {
            // 2.1 背景滚动
            bgMove();
            // 2.2 hero的操作  pc键盘
            heroMove();
            // 2.3 敌机的创建与运动
            enemyMove();
            // 2.4 子弹的创建与运动
            bulletMove();
        }, 30)
    }

启动项目,看一下子弹的效果:

70f1ed810de04e84af7c96a991af35bc.png

这样我们子弹的创建与运动就完成了,下一步就该判断子弹命中敌机后,销毁敌机的操作了。

 

7. 碰撞检测💨

我们在这一节要实现子弹与敌机相碰时,子弹和敌机都会销毁,如果 hero 和敌机相撞那就游戏结束了。首先我们创建一个 check.js 文件,在这里定义上述功能。

下面先理解一下判断是否碰撞的函数:

function isCrash(a,b) {
    var l1 = a.offsetLeft;
    var t1 = a.offsetTop;
    var r1 = l1 + a.offsetWidth;
    var b1 = t1 + a.offsetHeight;

    var l2 = b.offsetLeft;
    var t2 = b.offsetTop;
    var r2 = l2 + b.offsetWidth;
    var b2 = t2 + b.offsetHeight;
    if (r2<l1 || b2<t1 || r1<l2 || b1<t2) {
        // 不碰撞
        return false;
    } else {
        // 碰撞
        return true;
    }
}

在 if 语句里只要有一个条件不满足就说明不会碰撞,这个很好理解,这里我们就分析一下为什么 r2 < l1 就说明不会碰撞呢? l1 代表飞机到左侧背景的距离, l2 代表敌机到背景左侧的距离,那么 r2 < l1 的意思就是敌机本身的宽度再加上敌机到背景左侧的距离比飞机到背景左侧的距离还小,这样二者肯定不会碰上,所以其他方向同理。 

定义 check 函数判断敌机与hero,敌机与子弹是否碰撞:

function check() {
    // 1. hero与敌机
    // 2. 子弹与敌机
    var es = dEnemy.children;
    var bs = dBullet.children;
    for(var i=0; i<es.length; i++) {
        var e = es[i];
        // 英雄与敌机
        if (isCrash(dHero, e)) {
            // gameover
            alert('ganmeover');
            clearInterval(timer);
        }
        // 子弹与敌机
        for (var j=0; j<bs.length; j++) {
            var b = bs[j];
            if (isCrash(e,b)) {
                // 1. 子弹消失
                dBullet.removeChild(b);
                // 2. 敌机消失
                dEnemy.removeChild(e);
                i --;
                break;
            }
        }
    }
}

在 check 方法中我们调用 isCrash 方法校验英雄与敌机,子弹与敌机是否碰撞,如果英雄与敌机碰撞,我们就清除主页定时器,并执行 gameover 的弹窗。然后通过两个 for 循环,先遍历所有敌机,再对每一个子弹遍历,判断是否子弹和敌机碰撞,如果二者碰撞那就通过 removeChild 把移除元素。

将 check 方法加入定时器中:

function start() {
        timer = setInterval(function() {
            // 2.1 背景滚动
            bgMove();
            // 2.2 hero的操作  pc键盘
            heroMove();
            // 2.3 敌机的创建与运动
            enemyMove();
            // 2.4 子弹的创建与运动
            bulletMove();
            // 2.5 碰撞检测
            check();
        }, 30)
}

运行项目,看一下效果能否都实现:

45dbcb12a32f4fc39b90706911106c72.gif

在子弹在和敌机碰撞时,就达到了消灭敌机的效果,并且 hero 在与敌机相撞时也会弹窗提示游戏结束,这样我们游戏的主体部分就完成了,剩下的就是一个让人头疼的带本地存储功能的计分和排行榜功能了。

 

8. 统计得分💨

我们设置当子弹击毁敌机的时候得分就加一,得分会在游戏界面的左上角显示出来,这一节我们主要实现得分的这个功能,显示与样式这里先不关注。

因为在子弹和敌机碰撞的时候得分才会加一,所以这个功能应该添加在上一节的 check 方法之中

先在 check.js 中获取元素,定义得分变量 score:

var score = 0; //得分
var pScore = document.getElementById("score");

这里 pScore 获取的就是游戏界面左上角装载得分的盒子

然后是得分的逻辑实现:

for (var j=0; j<bs.length; j++) {
            var b = bs[j];
            if (isCrash(e,b)) {
                // 1. 子弹消失
                dBullet.removeChild(b);
                // 2. 敌机消失
                dEnemy.removeChild(e);
                // 3. 加分
                score ++;
                pScore.innerHTML = "得分:" + score;
                // 4. 处理数据
                i --;
                break;
            }
        }

现在当子弹命中敌机的时候,左上角的得分就会相应的加一。

 

9. 设置开始与结束界面💨

在游戏开始的时候应该先设置一个开始界面,然后可以输入昵称,这样方便后续结束游戏的时候设置排行榜。

下面是我们定义的开始界面,样式和 html 结构这里就不展示了,我们主要关注功能的实现:

9832c8fc8ce447b3bddf11884f9ea2a8.png

单击开始按钮的时候就会隐藏开始界面,然后调用 start 函数,star函数封装了定时器 timer :

    startBut.onclick = function() {
        if (iptNick.value === "") {
            alert("昵称不能为空");
            return ;
        }
        dStart.style.display = 'none';
        start();
    }

开始界面设置完后,我们就实现结束界面,先看一下结束界面的效果:


dbbbe545d15245e988393a5a1918bb14.png

在结束界面需要我们把最终得分还有排行榜输出出来,这里我们先不关系排行榜如何设置,先实现游戏结束的功能,当点击再来一次的时候,结束面板就会隐藏,弹出开始面板,因为我们知道结束面板的弹出和 hero 与敌机相撞这个事件是绑定的,所以我们可以把这些功能放在一个 gameover 函数中,当触发事件就调用这个函数。

在 index.html 中我们定义一个 gameover 函数:

//游戏结束
function gameover() {
        //停止计时
        clearInterval(timer);
        //修改本次得分 
        pShowScore.innerHTML = score;
        // 设置排行榜
        setPHB();
        // 显示结束面板
        dEnd.style.display = "block";
    }

如果游戏结束的话一定要先清除定时器 timer ,否则游戏还会继续进行,然后把最终得分展示在结束面板,然后设置排行榜,这里先定义一个 setPHB 方法,下一节我们再完善里面的功能,最后再显示结束面板,这样 gameover 函数就完成了。

当敌机与hero相撞时,调用gameover函数:

// 英雄与敌机
if (isCrash(dHero, e)) {
        // gameover
        gameover();
}

下面我们实现单击再来一次重新开始游戏的效果

首先肯定是点击它的时候让结束面板隐藏,显示开始面板,我们定义一个 again 方法:

    function again() {
        dEnd.style.display = "none";
        dStart.style.display = "block";
    }

但是这就完事了么?很明显没有,因为当你每次重新开始游戏的时候都应该让 hero 在起始的中间位置,我们再定义一个 setHeroPosition 方法:

var dHero = document.getElementById("hero");

//重新定位hero的位置
function setHeroPosition() {
    dHero.style.left = (sw-66)/2 + 'px';
    dHero.style.top = sh - 82 + 'px';
}

这个方法我们把它定义在 hero.js 文件中。

那现在重新开始游戏能正常实现了么?也没有,因为我们还得恢复所有数据:

    againBut.onclick = function() {
        again();
        //数据还原
        dis = 0;
        count = 0;
        dBullet.innerHTML = "";
        score = 0;
        pScore.innerHTML = "得分:0";
        dEnemy.innerHTML = "";
        setHeroPosition();
    }

在 index.html 中定义这个点击事件,先调用前面定义过的 again 方法,然后把所有我们计数用的变量初始化,再把画面中的所有子弹和敌机删除,最后调用 setHeroPosition 方法实现 hero 归位。

至此我们开始界面与结束界面的全部功能就都实现了。

 

10. 设置带本地存储功能的排行榜💨

我们先想一下这个排行榜应该怎么做,正常就是数据以对象存储在数组里,然后遍历显示在结束面板上。但是这样的话,如果我们刷新页面,所有的数据就被销毁了,那我们这个排行榜也就没有意义了,所以这里要通过 localStorage 本地存储实现。

在 gameover 函数中我们声明了一个 setPHB 方法,现在我们用 localStorage 来实现这个方法:

 function setPHB() {
        if (!localStorage.phb) {
            localStorage.phb = "[]";
        }
        var arr = JSON.parse(localStorage.phb);
        var isExit = -1; //昵称是否存在  -1表示不存在 
        for (var i=0; i<arr.length; i++) {
            if (arr[i].nick === iptNick.value) {
                // 存在
                isExit = i;
                break;
            }
        }
        if (isExit != -1) {
            // 更新数据
            arr[isExit].score = score;
        } else {
            //将新数据放入数组
            arr.push({
                nick: iptNick.value,
                score: score
            });
        }
        // 排序
        arr = arr.sort(function(a, b) {
            return b.score - a.score;
        })
        //设置ul的内容
        setUl(arr);

        //将新数据存入到本地
        localStorage.phb = JSON.stringify(arr);
    }

如果有小伙伴看不太懂,那听我先分析分析:

最开始那个 if 语句是什么意思?

它的意思就是因为localstorage本身就是存在的,所以我们只需要判断localStorage.phb存不存在,不存在就把他赋值为一个空数组。因为我们第一次游戏的话浏览器中肯定没有localStorage.phb,所以我们把它设置为空数组就行。

为什么这里的空数组还有带上引号呢? 

因为本地存储只能存储字符串,可以将对象JSON.stringify()编码后存储,或者通过JSON.parse()解析后获取数据

JSON.parse和JSON.stringify都是啥意思?

形象点说,就是JSON.parse方法可以把带字符串的玩意去掉字符串符号,比如原来是 " abc ",经过JSON.parse方法就能变成 abc。JSON.stringify()就是把这个过程反了过来。

通过 JSON.parse(localStorage.phb) 我们把本地存储的数据解码后拿出来,赋给变量 arr 。然后我们定义了一个变量 isExit 。如果排行榜上小张得了十分,下一次小张继续游戏得了十五分的话,那就得在排行榜上更新小张的得分记录,所以这里 isExit 就是干这个的。通过一个 for 循环判段 arr 数组里是否有开始界面输入的昵称相同的,有的话就通过索引更新数据,没有的话就把新的数据放进数组中。然后通过 sort 方法把数组排序,排行榜我们只取前三名。定义一个 setUI 方法把前三名记录在结束面板的排行榜上显示出来,最后再把数组 arr 通过 JSON.stringify() 给编码再存储回去。

下面我们完善 setUI 的代码,实现排行榜的显示效果:

    function setUl(arr) {
        ul.innerHTML = "";
        for (var i=0; i<arr.length; i++) {
            if (i > 2) {
                break;
            }
            var li = document.createElement("li");
            li.innerHTML = `
                    <span>${i+1}.</span>
                    <span>${arr[i].nick}</span>
                    <span>${arr[i].score}</span>
            `;
            ul.appendChild(li);
        }
    }

执行 setUI 的时候,我们先把排行榜清空重新排列,当 i>2 的时候说明是三名往后,就不用执行了直接 break 。排行榜的每条记录通过 innerHTML 插入排名,昵称和得分。

这样我们的飞机大战就全部完工啦!

 

源码地址:

https://gitee.com/jie_shao1112/aircraft-warhttps://gitee.com/jie_shao1112/aircraft-war

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/406871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

疫情散去想看电影,使用css3动画实现一个阿凡达2完美开场

在历经了艰苦卓绝的3年抗疫后&#xff0c;疫情终于还是来了&#xff0c;很多小伙伴变成了小洋人酸奶&#xff0c;我相信过不了多少天&#xff0c;疫情终将散去&#xff0c;那个时候就可以和家人走进电影院啦。 今天用css布局一个阿凡达2的影院场景&#xff0c;提前过一过瘾。 目…

《uni-app》表单组件-form表单

本文分享的Form组件为uni-app的内置组件Form&#xff0c;非扩展组件&#xff0c;两者在用法上其实大同小异&#xff0c;只是扩展组件的属性以及事件更多…没有本质上的区别&#xff5e; 《uni-app》表单组件-form表单一. 简介二. 基础用法三. submit事件四. reset事件五. repor…

Tomcat安装配置及IDEA配置方法【亲测有效】

Tomcat安装配置及IDEA配置1.下载Tomcat2.配置Tomcat环境变量3.安装Tomcat4.启动Tomcat5.测试Tomcat6.IDEA配置Tomcat1.下载Tomcat Tomcat9官网下载地址 选择自己需要的版本&#xff0c;一般选择Windows 64位压缩包版本&#xff1a; 下载完后安装解压即可&#xff0c;解压后的…

如何使用nvm切换node版本

我比较懒惰,如非必要,不喜欢npm版本切换来切换去,感觉浪费我编程的时间.后来发现,现在偷的懒都是为将来的忙碌埋下的祸根. 言归正传,本文主要是讲解一下,如何使用nvm进行npm版本的切换. 工欲善其事必先利其器,我们先下载nvm;直接上下载链接(针对windows哈,毕竟我没mac本); 下载…

Code For Better 谷歌开发者之声——初识Web与谷歌,拉起兴趣之心。

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;程序是我的生命,但我相信爱她甚过爱我的生命。 目录一、了解谷歌浏览器1. 简介2.优点二、认识Web1. 简介2. 特点2. 网…

【云原生 | 21】Docker运行Web服务实战之Apache

作者简介&#xff1a;&#x1f3c5;云计算领域优质创作者&#x1f3c5;新星计划第三季python赛道第一名&#x1f3c5; 阿里云ACE认证高级工程师&#x1f3c5; ✒️个人主页&#xff1a;小鹏linux &#x1f48a;个人社区&#xff1a;小鹏linux&#xff08;个人社区&#xff09;欢…

【微信小程序】如何获得自己当前的定位呢?本文利用逆地址解析、uni-app带你实现

目录 前言 效果展示 一、在腾讯定位服务配置微信小程序JavaScript SDK 二、使用uni-app获取定位的经纬度 三、 逆地址解析&#xff0c;获取精确定位 四、小提示 前言 效果展示 一、在腾讯定位服务配置微信小程序JavaScript SDK 在浏览器搜索腾讯定位服务&#xff0c;找…

React(四) ——hooks的使用

&#x1f9c1;个人主页&#xff1a;个人主页 ✌支持我 &#xff1a;点赞&#x1f44d;收藏&#x1f33c;关注&#x1f9e1; 文章目录⛳React Hooks&#x1f4b8;useState(保存组件状态)&#x1f948;useEffect(处理副作用)&#x1f50b;useCallback&#xff08;记忆函数&#…

Sass 和 SCSS

▣Sass (Syntactically Awesome StyleSheets)&#xff0c;是由buby语言编写的一款css预处理语言&#xff0c;和html一样有严格的缩进风格&#xff0c;和css编写规范有着很大的出入&#xff0c;是不使用花括号和分号的&#xff0c;所以不被广为接受。 Sass 是一款强化 CSS 的辅助…

使用Nodejs搭建HTTP服务,并实现公网远程访问「内网穿透」

文章目录前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口5.固定公网地址前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff08;原…

微信小程序|基于小程序+云开发制作一个菜谱小程序

今天吃什么?这是一个让强迫症左右为难的问题,跟随此文基于小程序+云开发制作一个菜谱小程序,根据现有食材一键生成菜谱,省心又省力。 一、小程序1. 创建小程序</

【python】喜欢XJJ?这不得来一波大采集?

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 俗话说的好&#xff1a;技能学了~就要用在自己喜欢得东西上&#xff01;&#xff01; 这我不得听个话~我喜欢小姐姐&#xff0c;跳舞的小姐姐 这不得用python把小姐姐舞采集下来~嘿嘿嘿 完整源码、素材皆可点击文章下方名片…

Java web学生信息管理系统(jsp)

&#x1f95e;目录 &#x1f36c;1 概述 1.1课程设计目的 1.2预备知识 JAVAWeb&#xff1a; MySQL&#xff1a; JSP&#xff1a; 1.3实训的内容和要求 &#x1f36c;2 需求分析 2.1系统目标 2.2功能分析&#xff1a; 2.3开发环境&#xff1a; &#x1f36c;3 设计步…

大数据热点城市波动图案例【CSS3实现 + 原理分析 + 源码获取】

一&#xff1a;案例效果 本次案例我们分析一下数据可视化页面最常见的热点图是如何实现的&#xff0c;其原理并不复杂&#xff0c;只需要用到CSS3动画属性 animation 以及 keyframe 关键帧即可&#xff0c;重点是向外扩散的环如何布局比较合适&#xff0c;以及每个环怎么扩散何…

HTML 5标签搭建页面结构--1

本文标签: HTML基本语法 排版标签 媒体标签 链接标签 文章目录 前言 一、基础认识 1.认识网页 2.微博标准 3.web标准的构成 4.小结 二、HTML基本结构解读 2.HTML标签及结构 1.标签的结构: 2.排版标签: 2.文本格式化标签: 3.媒体标签: 1.图像相关标签: 2.图片标签的 title …

JavaScript-百炼成仙(第1节掌握JavaScript基础1.1-1.21)

文章目录1.1 第一章 初入宗门1.2 第二章 直接量1.3 第三章 数据类型1.4 第四章 数据类型扩展内容:1.5 第五章 基础考核1.6 第六章 何老1.7 第七章 对象数据类型1.8 第八章 对象的取值1.9 第九章 循环遍历的奥妙小结&#xff1a;For 循环1.10 第十章 对象内容的遍历1.11 第十一章…

30个题型+代码(冲刺2023蓝桥杯)(上)

愿意的可以跟我一起刷&#xff0c;每个类型做1~5题 &#xff0c;4月前还可以回来系统复习 目录 &#x1f34e;注意 &#x1f33c;前言 &#x1f33c;一&#xff0c;前缀和 &#x1f44a;&#xff08;一&#xff09;3956. 截断数组 &#x1f333;Time Limit Exceeded &a…

如何用python代码,更改照片尺寸,以及更换照片底色

前言 python浅浅替代ps&#xff1f;如何用代码来p证件照并且更换底色&#xff1f; 唉&#xff0c;有个小姐姐给我扔了张照片&#xff0c;叫我帮忙给她搞成证件照的尺寸还得换底色&#xff0c;她说自己忙的很 可惜电脑上没有ps只有pycharm&#xff0c;没得办法只能来试试看代…

没有关系的话,那就去建立关系吧

今天给大家分享一道链表的好题--链表的深度拷贝&#xff0c;学会这道题&#xff0c;你的链表就可以达到优秀的水平了。力扣 先来理解一下题目意思&#xff0c;即建立一个新的单向链表&#xff0c;里面每个结点的值与对应的原链表相同&#xff0c;并且random指针也要指向新链表中…

vite基础使用及相关配置

前言 这篇文章主要是记录前段时间公司里以vite构建的一个小项目&#xff08;前端界面不多&#xff0c;主要功能及相关配置是在后端&#xff09;&#xff0c;挺简单的几个小页面。 说到vite&#xff0c;之前虽然都有学习了解及demo尝试&#xff0c;但因为业务等其他各方面因素也…