前言

前面2个章节已经封装好了摇杆和NES虚拟按键,现在配合jsnes这个包来完成一个移动端版的NES模拟器。

这是插件的github地址:bfirsh/jsnes

这个包可以直接拿来用,但是没有适配移动端。他通过事件监听,判断evt.keyCode属性来判断用户的输入信息。在移动端只要进行事件监听,生成一个带有keyCode属性的evt,然后将evt这个对象传递给相关的事件回调即可,唯一要做的就是要为不同的按钮匹配相应的keyCode,才能让游戏动起来。

1.jsnes牛刀小试

在下载完jsnes的包之后,打开example文件夹的html文件,就可以看到使用方法了

<!DOCTYPE html>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Embedding Example</title>
<!-- 模拟器的包 -->
<script type="text/javascript" src="https://unpkg.com/jsnes/dist/jsnes.min.js"></script>
<!-- 模拟器使用说明 -->
<script type="text/javascript" src="nes-embed.js"></script>
<script>window.onload = function(){nes_load_url("nes-canvas", "InterglacticTransmissing.nes");}</script>
</head>
<body>
<div style="margin: auto; width: 75%;">
<canvas id="nes-canvas" width="256" height="240" style="width: 100%"/>
</div>
<p>DPad: Arrow keys<br/>Start: Return, Select: Tab<br/>A Button: A, B Button: S</p>
</body>
</html>

其中最重要的是nes-embed.js,这是截取一部分重要的代码出来

入口函数:封装了一个函数,传入canvas元素的标识和rom的url地址,通过ajax来获取到对应url的Rom数据

function nes_load_url(canvas_id, path){
//
nes_init(canvas_id); var req = new XMLHttpRequest();
req.open("GET", path);
req.overrideMimeType("text/plain; charset=x-user-defined");
req.onerror = () => console.log(`Error loading ${path}: ${req.statusText}`); req.onload = function() {
if (this.status === 200) {
//装载Rom数据
nes_boot(this.responseText);
} else if (this.status === 0) {
// Aborted, so ignore error
} else {
req.onerror();
}
}; req.send();
}

键盘事件监听,调用keyboard()函数

document.addEventListener('keydown', (event) => {keyboard(nes.buttonDown, event)});
//'keyup'用来取消动作 对于AB键,没有他则无法进行下一轮操作 对于方向键,没有他则游戏角色会一直朝一个方向走下去
document.addEventListener('keyup', (event) => {keyboard(nes.buttonUp, event)});

keyboard()函数:对事件对象的keyCode进行判断,在这里自定义按键

function keyboard(callback, event){
var player = 1; //1表示1p 2表示2p
switch(event.keyCode){
case 38: // UP 87代表W
callback(player, jsnes.Controller.BUTTON_UP); break;
case 40: // Down 83代表S
callback(player, jsnes.Controller.BUTTON_DOWN); break;
case 37: // Left
callback(player, jsnes.Controller.BUTTON_LEFT); break;
case 39: // Right
callback(player, jsnes.Controller.BUTTON_RIGHT); break;
case 65: // 'a' - qwerty, dvorak
case 81: // 'q' - azerty
callback(player, jsnes.Controller.BUTTON_A); break;
case 83: // 's' - qwerty, azerty
case 79: // 'o' - dvorak
callback(player, jsnes.Controller.BUTTON_B); break;
case 9: // Tab
callback(player, jsnes.Controller.BUTTON_SELECT); break;
case 13: // Return
callback(player, jsnes.Controller.BUTTON_START); break;
default: break;
}
}

这是我自己的设定,用WSAD代表上下左右,JK代表BA,空格代表选择键 回车代表开始键

function keyboard(callback, event){
var player = 1;
//console.log(event.keyCode)
switch(event.keyCode){
case 87: // UP - W
callback(player, jsnes.Controller.BUTTON_UP); break;
case 83: // Down -- S
callback(player, jsnes.Controller.BUTTON_DOWN); break;
case 65: // Left -- A
callback(player, jsnes.Controller.BUTTON_LEFT); break;
case 68: // Right -- D
callback(player, jsnes.Controller.BUTTON_RIGHT); break;
case 75: // K
callback(player, jsnes.Controller.BUTTON_A); break;
case 74: // J
callback(player, jsnes.Controller.BUTTON_B); break;
case 32: // 空格
callback(player, jsnes.Controller.BUTTON_SELECT); break;
case 13: // 回车
callback(player, jsnes.Controller.BUTTON_START); break;
default: break;
}
}

另外这个插件有2个位置需要优化,其一:AudioContext需要兼容处理

//93行
var audio_ctx = new window.AudioContext();

改成

var contextClass =(window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext);
var audio_ctx = new contextClass();

其二:某些浏览器打开时提示:

The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page

查阅资料得知:谷歌浏览器从71版本开始就开启了谷歌的安全策略,因此导致的声音不能自动播放,必须在用户有了操作之后才可以播放音乐。可以设置一个按钮,在按钮的点击事件中调用

document.querySelector('#btn_load').onclick = function(){
//加载游戏
nes_load_url("nes-canvas", "./roms/超级玛丽.nes")
//隐藏加载按钮
this.style.display = 'none'
}

2.NES模拟器完整代码

HTML文件

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- nes_btn样式文件 -->
<link rel="stylesheet" href="./css/nes_btn.css">
<!-- 该页面样式文件 -->
<link rel="stylesheet" href="./css/index.css">
<title>NES网页模拟器</title>
</head>
<body>
<div id="show_box">
<h3 id="name">热血格斗</h3>
<div class="canvas_box">
<h3 id="btn_load">点此加载游戏</h3>
<canvas id="nes-canvas" width="256" height="240" style="width: 100%"/>
</div>
</div>
<div id="control_box">
<!-- 摇杆容器 -->
<div id="direction"></div>
<!-- 按键容器 -->
<div id="user_btn_box"></div>
</div>
</body>
</html>
<script src="./js/jsnes.min.js"></script>
<script src="./js/nes-embed.js"></script>
<!-- nes_btn插件 -->
<script src="./js/nes_btn.js"></script>
<!-- 摇杆插件 -->
<script src="./js/nipplejs.min.js"></script>
<script src="./js/joystick.js"></script>
<script>
window.onload = function(){
//监听加载按钮
document.querySelector('#btn_load').onclick = function(){
//加载游戏
nes_load_url("nes-canvas", "./roms/超级玛丽.nes")
//隐藏加载按钮
this.style.display = 'none'
} //实例化摇杆
var joystick = new Joystick({
el:"#direction",//容器
color:" royalblue",//摇杆颜色
size:100,//摇杆大小
isFourBtn:false,//8键模式
keyCodes:[87, 83, 65, 68],//绑定 上下左右 到 WSAD键
btn_down_fn:(event) => {keyboard(nes.buttonDown, event)},//按下时的回调
btn_up_fn:(event) => {keyboard(nes.buttonUp, event)},//释放时的回调
relative:true,//默认为true 会将容器设置为相对定位
})
joystick.init() //实例化NES按钮
var nesBtn = new VirtualNesBtn({
el:"#user_btn_box", //容器
btn_down_fn:(event) => {keyboard(nes.buttonDown, event)},//虚拟按钮按下时的回调 参数evt
btn_up_fn:(event) => {keyboard(nes.buttonUp, event)},//虚拟按钮弹起时的回调 参数evt
keyCodes:[32,13,74,75] //按顺序分别是 select start b a
}) //NES按钮实例初始化
nesBtn.init()
}
</script>

css/nes_btn.css

*{
-webkit-touch-callout:none;
-webkit-user-select:none;
-khtml-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
}
html,body{
padding:0;
margin: 0;
}
#show_box{
position: relative;
width:80%;
max-width: 512px;
text-align: center;
margin: 20px auto;
}
#show_box .canvas_box{
background: url(../imgs/unload_bg.jpg);
background-size: 100% 100%;
}
#show_box #btn_load{
position: absolute;
color:#FF4500;
left:50%;
top:50%;
transform: translate(-50%,-50%);
cursor: pointer;
}
#control_box {
width:100%;
padding-top: 30px;
padding-bottom: 40px;
bottom:0;
left:0;
position: fixed;
overflow: hidden;
display: flex;
justify-content: space-around;
}
#control_box #direction{
width:40vw;
height:40vw;
position: relative;
}
#control_box #user_btn_box{
width:48%;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-content: space-around;
}

css/index.css

*{
-webkit-touch-callout:none;
-webkit-user-select:none;
-khtml-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
}
html,body{
padding:0;
margin: 0;
}
#show_box{
position: relative;
width:80%;
max-width: 512px;
text-align: center;
margin: 20px auto;
}
#show_box .canvas_box{
background: url(../imgs/unload_bg.jpg);
background-size: 100% 100%;
}
#show_box #btn_load{
position: absolute;
color:#FF4500;
left:50%;
top:50%;
transform: translate(-50%,-50%);
cursor: pointer;
}
#control_box {
width:100%;
padding-top: 30px;
padding-bottom: 40px;
bottom:0;
left:0;
position: fixed;
overflow: hidden;
display: flex;
justify-content: space-around;
}
#control_box #direction{
width:40vw;
height:40vw;
position: relative;
}
#control_box #user_btn_box{
width:48%;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-content: space-around;
}

js/nes_btn.js

function VirtualNesBtn(opt){
//接收容器的标识
this.el = opt.el
//接收回调
this.btn_down_fn = opt.btn_down_fn //fn
this.btn_up_fn = opt.btn_up_fn //fn //保存按钮信息
this.btns_info = [] //为5个按钮添加带数字后缀的 id
this.btns_info = [{},{},{},{},{}]
this.btns_info[0].id = 'btn_select_' + Math.floor(Math.random() * 100000)
this.btns_info[1].id = 'btn_start_' + Math.floor(Math.random() * 100000)
this.btns_info[2].id = 'btn_ab_' + Math.floor(Math.random() * 100000)
this.btns_info[3].id = 'btn_b_' + Math.floor(Math.random() * 100000)
this.btns_info[4].id = 'btn_a_' + Math.floor(Math.random() * 100000) //设定5个按钮的文本名称 name
this.btns_info[0].name = 'SELECT'
this.btns_info[1].name = 'START'
this.btns_info[2].name = 'B + A'
this.btns_info[3].name = 'B'
this.btns_info[4].name = 'A' //配置5个按钮的 keycode 默认为 空格 回车 J K
this.btns_info[0].keyCode = opt.keyCodes && opt.keyCodes[0] || 32
this.btns_info[1].keyCode = opt.keyCodes && opt.keyCodes[1] || 13
this.btns_info[2].keyCode = 'macro_key'
this.btns_info[3].keyCode = opt.keyCodes && opt.keyCodes[2] || 74
this.btns_info[4].keyCode = opt.keyCodes && opt.keyCodes[3] || 75 } //初始化 创建虚拟按钮
VirtualNesBtn.prototype.init = function(opt){
var me = this
//创建5个按键
var btn_select = document.createElement('span')
var btn_start = document.createElement('span')
var btn_ab = document.createElement('span')
var btn_b = document.createElement('span')
var btn_a = document.createElement('span') //为5个按键设置css类名
btn_select.classList.add('btn','btn-select')
btn_start.classList.add('btn','btn-start')
btn_ab.classList.add('btn','btn-ab')
btn_b.classList.add('btn','btn-b')
btn_a.classList.add('btn','btn-a') //为5个按键设置id
btn_select.id = me.btns_info[0].id
btn_start.id = me.btns_info[1].id
btn_ab.id = me.btns_info[2].id
btn_b.id = me.btns_info[3].id
btn_a.id = me.btns_info[4].id //为5个按键设置 文本
btn_select.innerText = me.btns_info[0].name
btn_start.innerText = me.btns_info[1].name
btn_ab.innerText = me.btns_info[2].name
btn_b.innerText = me.btns_info[3].name
btn_a.innerText = me.btns_info[4].name //创建容器,并将5个按钮插入其中
var nes_btn_box = document.createElement('div')
nes_btn_box.classList.add('nes_btn_box')
nes_btn_box.appendChild(btn_select)
nes_btn_box.appendChild(btn_start)
nes_btn_box.appendChild(btn_ab)
nes_btn_box.appendChild(btn_b)
nes_btn_box.appendChild(btn_a) //插入到目标容器中
var target = document.querySelector(me.el)
target.appendChild(nes_btn_box) //设置touch事件监听
target.addEventListener('touchstart',(evt) => {
//阻止默认事件,防止快速点击时页面放大
evt.preventDefault() //判断点中的是否是虚拟按钮
var is_nes_btn = me.btns_info.some(function(item){
return item.id === evt.target.id
}) if(is_nes_btn){
//添加高亮
evt.target.classList.add('isTouch')
//处理此次点击
me.handleBtn(evt,'btn_down')
}
}) target.addEventListener('touchend',(evt) => {
//判断点中的是否是虚拟按钮
var is_nes_btn = me.btns_info.some(function(item){
return item.id === evt.target.id
}) if(is_nes_btn){
//移除高亮
evt.target.classList.remove('isTouch')
//处理此次点击
me.handleBtn(evt,'btn_up')
}
})
} //对虚拟按键的id进行判断,返回要绑定的 keyCode
VirtualNesBtn.prototype.getCode = function(id){
var me = this
//1.根据id查到按钮信息在数组中的下标
var index = me.btns_info.findIndex(function(item){
return item.id === id
})
//2.根据下标找到对应的keyCode
return me.btns_info[index].keyCode
} //对按键进行处理
VirtualNesBtn.prototype.handleBtn = function(evt,type){
var me = this
//1.找到keycode
var keyCode = me.getCode(evt.target.id)
//2.根据keycode判是否是宏按键
if(keyCode === 'macro_key'){
//要触发2个按键
var evt_tem = {}
var evt_tem2 = {}
evt_tem.keyCode = me.btns_info[3].keyCode
evt_tem2.keyCode = me.btns_info[4].keyCode
if(type === 'btn_down'){
me.btn_down_fn && me.btn_down_fn(evt_tem)
me.btn_down_fn && me.btn_down_fn(evt_tem2)
}else{
me.btn_up_fn && me.btn_up_fn(evt_tem)
me.btn_up_fn && me.btn_up_fn(evt_tem2)
}
}else{
//添加keyCode属性
evt.keyCode = keyCode
//不是宏按键则执行相应的回调
if(type === 'btn_down'){
me.btn_down_fn && me.btn_down_fn(evt)
}else{
me.btn_up_fn && me.btn_up_fn(evt)
}
}
}

js/joystick.js

//此插件依赖nipplejs.min.js
//by https://gitee.com/lianlizhou function Joystick(opts){
//默认配置
var position = {left:"50%", top:"50%"}
//保存传入的配置信息
this.el = opts && opts.el
this.color = opts && opts.color || 'white'
this.size = opts && opts.size || 100 this.isFourBtn = opts && opts.isFourBtn ? true : false //默认4键模式 否则8键模式(左上/左下/右上/右下)
this.keyCodes = opts && opts.keyCodes || [87, 83, 65, 68] //按顺序是上下左右 默认WSAD
this.btn_down_fn = opts && opts.btn_down_fn //fn 按下时的回调
this.btn_up_fn = opts && opts.btn_up_fn //fn 释放时的回调
this.relative = opts && opts.relative || true //默认将容器设置为相对定位 //生成配置信息 这里配置的参数将传给nipplejs.min.js
this.opts = {
zone:document.querySelector(this.el), //用户设置的容器
mode: 'static',//模式,static就是摇杆中心固定
position:{left:'50%',top:'50%'},//让摇杆容器定位到用户设置容器的正中心
color:this.color,//摇杆的颜色 包括back和front nipplejs默认白色,通过背景色实现
size:this.size,//摇杆容器back元素的大小,front是他的一半,默认100
} //记录上一次按键方向
this.direction = null //手势释放时重新设为null
} Joystick.prototype.init = function(){
var me = this //如果用户不阻止,则将用户容器设为相对定位 要在实例创建前设置
if(me.relative) document.querySelector(me.el).style.position = 'relative' //创建nipplejs实例
var manager = nipplejs.create(me.opts) //事件监听
manager.on('start',function(evt,data){
})
manager.on('move',function(evt,data){
//数据交给其他方法处理
me.onMove && me.onMove(data)
})
manager.on('end',function(evt,data){
me.onEnd && me.onEnd()
})
//阻止默认事件,防止快速点击时页面缩放
document.querySelector(me.el).addEventListener('touchstart',function(evt){
evt.preventDefault()
})
} Joystick.prototype.onMove = function(data){
var me = this
//通过distance属性是否存在判断此次操作是否有效
if(!data.distance) return //获取最新方向信息
var now_direction = me.getDirection(data)
//处理方向信息
me.handleDirection(now_direction,me.direction) //新方向 上一个方向
//更新按键方向
me.direction = now_direction
} Joystick.prototype.onEnd = function(){
var me = this
//1.获取要处理的keyCode数组 并调用方法将相关按键释放
me.handleCodeArr('up',me.getCodeArr(me.direction)) //up or down
//2.重置方向信息
me.direction = null
} Joystick.prototype.getDirection = function(data){
var me = this
//判断是4键模式还是8键模式
if(me.isFourBtn){
//4键模式 直接返回
return data.direction.angle
}else{
//8键模式 根据角度值返回对应的方向
return me.transformDirection(data.angle.degree)
}
} //用于8键模式 将角度转换成方向
Joystick.prototype.transformDirection = function(degree){
//8个方向平方360度 每个方向45度
//右上 22.5 - 76.5
//上 76.5 - 112.5
//左上 112.5 - 157.5
//左 157.5 - 202.5
//左下 202.5 - 247.5
//右下 247.5 - 292.5
//右 >292.5 <=22.5
if(degree > 292.5){
//右
return 'right'
}else if(degree > 247.5){
//右下
return 'right_down'
}else if(degree > 202.5){
//左下
return 'left_down'
}else if(degree > 157.5){
//左
return 'left'
}else if(degree > 112.5){
//左上
return 'left_up'
}else if(degree > 76.5){
//上
return 'up'
}else if(degree > 22.5){
//右上
return 'right_up'
}else{
//右
return 'right'
}
} //将相关方式信息转换为keyCode,并放入数组中
Joystick.prototype.handleDirection = function(new_direction,old_direction){
var me = this
//old_direction可能为null 但new_direction绝对有值
//当old_direction时,说明用户刚开始点击,此时需要将相应的keyCode传给btn_down_fn执行
if(old_direction === null){
var code_arr = me.getCodeArr(new_direction)
me.handleCodeArr('down',code_arr)
}
//当old_direction不为null,说明用户正在滑动 如果此时新旧方向不一致,则要更新按键状态
if(old_direction !== null && new_direction !== old_direction){
var old_arr = me.getCodeArr(old_direction)
var new_arr = me.getCodeArr(new_direction)
//找出已经发生改变的方向 例如 右上 -> 右下 需要将'上'取消掉,同时将'下'按下 //遍历新数组的元素,对比该元素是否存在旧数组中,如果不存在,即可得到 按下的 code_arr
var down_arr = new_arr.filter( code => {
return !old_arr.includes(code)
})
me.handleCodeArr('down',down_arr)
//遍历旧数组的元素,对比该元素是否存在新数组中,如果不存在,即可得到 释放的 code_arr
var up_arr = old_arr.filter( code => {
return !new_arr.includes(code)
})
me.handleCodeArr('up',up_arr)
}
} //将方向信息转换为keyCode后,以数组形式返回
Joystick.prototype.getCodeArr = function(direction){
var me = this
switch(direction){
case 'up':return [me.keyCodes[0]];break;
case 'down':return [me.keyCodes[1]];break;
case 'left':return [me.keyCodes[2]];break;
case 'right':return [me.keyCodes[3]];break;
case 'right_up':return [me.keyCodes[3], me.keyCodes[0]];break;
case 'right_down':return [me.keyCodes[3], me.keyCodes[1]];break;
case 'left_up':return [me.keyCodes[2], me.keyCodes[0]];break;
case 'left_down':return [me.keyCodes[2], me.keyCodes[1]];break;
default:break;
}
} Joystick.prototype.handleCodeArr = function(type,arr){
//type为up or down
//arr为需要处理的包含keyCode的数组 var me = this
var fn = me.btn_down_fn //默认为按下时的回调
if(type !== 'down'){
//如果不是down 说明是手势释放 需要调用释放按键的回调
fn = me.btn_up_fn
} //遍历数组中的keyCode 逐个处理
for(var i=0;i<arr.length;i++){
//对keyCode进行包裹后
fn && fn(me.package(arr[i]))
}
} //对keyCode进行封装
Joystick.prototype.package = function(keyCode){
var evt = {}
evt.keyCode = keyCode
return evt
}

移动端NES网页模拟器(3)的更多相关文章

  1. 使用Chrome开发者工具调试Android端内网页(微信,QQ,UC,App内嵌页等)

    使用Chrome开发者工具调试Android端内网页(微信,QQ,UC,App内嵌页等) 前言 移动端页面调试一直是好多朋友头疼的问题,iOS 由于其封闭的特性和整体较高的性能,整体适配相对好做,调试 ...

  2. 移动端 Web 网页调试技巧

    原文出处: 盛瀚钦 本文主要列举了调试本地网页.查看测试环境网页的各种方法,涵盖了PC.iPad.移动端的调试技巧. 本文的不足之处在于,小溪里暂时还没有找到调试位于微信中和安卓各国产浏览器上的网页. ...

  3. 借助FreeHttp为任意移动端web网页添加vConsole调试

        以下介绍在不用修改代码并发布项目的情况下,为我们日常使用的移动web应用(如手机web淘宝)添加vConsole调试工具的方法   vConsole介绍 vConsole是一个轻量.可拓展.针 ...

  4. Appium移动端自动化测试--搭建模拟器和真机测试环境

    详细介绍安装Android Studio及Android SDK.安装Appium Server. 文章目录如下 目录 文章目录如下 模拟器--安装Android Studio及Android SDK ...

  5. HTML5和CSS3 PC端静态网页琐碎知识点

    1.PC端为了兼容IE9以及IE9以下,尽量要使用float进行布局,兼容性好,一般不要用flex进行布局. 2.问起CSS选择器的分类,先说id选择器,类选择器,属性选择器,伪类选择器,伪元素选择器 ...

  6. C#搞个跨平台的桌面NES游戏模拟器

    支持Windows,Mac,Linux    NES模拟器内核源码来自 https://github.com/colinvella/EmuNes   他这边的源码功能很完善了的,支持视频录制,手柄,金 ...

  7. 【转载】小tips: PC端传统网页试试使用Zepto.js进行开发

    Zepto.js设计之初专为移动端,不对一些古董浏览器支持.所以,尺寸很小,压缩后20K多一点,但是,jQuery压缩后,3.*版本要80多K,1.*版本则要90多K,4倍差距. 由于每个页面都会使用 ...

  8. JS判断PC还是移动端打开网页

    最近在做移动端网站,也需兼容PC端.还没找到更好的方法,只能用javascr判断用户是在PC端打开还是移动端打开. JS判断 var isPC = function (){    var userAg ...

  9. 如何实用便捷的在本地真机调试WEB端HTML5网页

    先简单介绍两款常用但需要一定条件或限制的工具 1.如果你能FQ chrome在32版本后就自带了移动端调度工具,可以在Android直接联调,但唯一遗憾的是,在我大天朝要FQ后才能行的通,我自己试了后 ...

  10. 移动端调试 — chrome模拟器基础调试

    打开开发者工具,进入chrome调试状态,点击左上角的手机图标,进入手机模拟器调试状态. 模拟器支持操作: 切换设备类型,模拟网络环境,模拟bar,keyboard弹出状态,横屏状态,更改UserAg ...

随机推荐

  1. Yarn 3.0 Plug'n'Play (PnP) 安装和迁移

    前言 以前用 npm, 后来 yarn 火了就用 yarn. 后来 yarn 2.0 大改版, Angular 不支持就一直没用. 一直到去年的 Angular 13 才开始支持. 最近又开始写 An ...

  2. 学习问题记录:RocketMQ集成到SpringBoot后,消费者无法自动进行消息消费。

    情况说明 在SpringBoot中集成了RocketMQ,实践过程中,通过RocketMQ DashBoard观察,生产者可以正常将进行消息提交:通过日志及DashBoard观察,消费者成功在Rock ...

  3. 10-04 NOIP模拟赛

    10-04 NOIP模拟赛 喜提80+30+10=120分 T1 水管(flow) 题目描述 某国有 \(n\) 个城市,水利调配非常有问题,这导致部分地区出现严重的缺水,而部分地区却全年洪灾泛滥.政 ...

  4. slab分配器正式被弃用,slub成为分配器唯一选择

    在使用slab分配器进行内存分配时,可能会出现以下缺点: 内存碎片化.由于slab分配器需要将内存分成大小相同的块,如果分配不均衡或者对象大小不同,就容易导致内存碎片化. 性能下降.Slab分配器将内 ...

  5. 5.7 函数y=Asin(ωx+φ)的图像和性质

    \({\color{Red}{欢迎到学科网下载资料学习 }}\) [ [高分突破系列]高一数学上学期同步知识点剖析精品讲义与分层练习] (https://www.zxxk.com/docpack/27 ...

  6. FirewallD is not running 原因与解决方法

    解决方法关于linux系统防火墙: centos5.centos6.redhat6系统自带的是iptables防火墙.centos7.redhat7自带firewall防火墙.ubuntu系统使用的是 ...

  7. computed 中的属性名和data的属性名可以相同吗?

    不可以,因为无论computed 中的属性名还是 data 又或者是props中的属性名,都会别挂载到组件实例上,所以名字都不允许重复  : ps:好比一个作用域里面不允许定义2个相同的变量名 :

  8. 谈谈 keep-alive 组件,以及它们的实现原理

    keep-alive 组件的使用场景: 我们切换 2 个组件的时候,2个组件会轮流被销毁创建,但是现在需求,切换到一个组件,另一个组件不会别销毁,会保留原来的状态 :就要使用 vue 内置的组件 ke ...

  9. Python之爬虫-全民k歌

    import re import os import requests from aip import AipSpeech from pydub import AudioSegment APP_ID ...

  10. ToDesk云电脑堪比万元PC?黑悟空、老头环及战锤40K实测体验!

    2009年,OnLive首次在旧金山游戏开发者大会推出"云游戏"的概念,但受限于当时的网络宽带技术,云游戏并不被十分看好.现如今,5G时代已然到来,数据通量和画质传输给予云游戏崛起 ...