Rewind replay & compress icons
14
README.md
@ -63,6 +63,20 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏!
|
||||
|
||||
## 更新说明
|
||||
|
||||
### 2018.4.19 V2.1
|
||||
|
||||
* [x] 编辑器添加新建和删除按钮;地图自动保存
|
||||
* [x] 录像支持倒退(录像播放中每20步存一个节点,最多30个)
|
||||
* [x] Gif支持:可以作为楼层背景图或者使用显示动图事件
|
||||
* [x] 图片显示增加淡入淡出效果
|
||||
* [x] APP端也能下载或读取文件
|
||||
* [x] 地图临界显伤
|
||||
* [x] 单个存档清理
|
||||
* [x] 大数据魔塔的支持
|
||||
* [x] 进一步对JS文件和图标进行压缩
|
||||
* [x] 修复有时候无法输入ID的问题
|
||||
* [x] 其他细节优化
|
||||
|
||||
### 2018.3.17 V2.0.1
|
||||
|
||||
* [x] 道具使用效果的进一步分离
|
||||
|
||||
34
index.html
@ -43,43 +43,43 @@
|
||||
</div>
|
||||
<div id='statusBar' class="clearfix">
|
||||
<div class="status" id="floorCol">
|
||||
<img src='project/images/floor.png' id="img-floor">
|
||||
<img id="img-floor">
|
||||
<p class='statusLabel' id='floor'></p>
|
||||
</div>
|
||||
<div class="status" id="lvCol">
|
||||
<img src='project/images/lv.png' id="img-lv">
|
||||
<img id="img-lv">
|
||||
<p class='statusLabel' id='lv'></p>
|
||||
</div>
|
||||
<div class="status" id='hpmaxCol'>
|
||||
<img src='project/images/hpmax.png' id="img-hpmax">
|
||||
<img id="img-hpmax">
|
||||
<p class='statusLabel' id='hpmax'></p>
|
||||
</div>
|
||||
<div class="status">
|
||||
<img src='project/images/hp.png' id="img-hp">
|
||||
<img id="img-hp">
|
||||
<p class='statusLabel' id='hp'></p>
|
||||
</div>
|
||||
<div class="status">
|
||||
<img src='project/images/atk.png' id="img-atk">
|
||||
<img id="img-atk">
|
||||
<p class='statusLabel' id='atk'></p>
|
||||
</div>
|
||||
<div class="status">
|
||||
<img src='project/images/def.png' id="img-def">
|
||||
<img id="img-def">
|
||||
<p class='statusLabel' id='def'></p>
|
||||
</div>
|
||||
<div class="status" id="mdefCol">
|
||||
<img src='project/images/mdef.png' id="img-mdef">
|
||||
<img id="img-mdef">
|
||||
<p class='statusLabel' id='mdef'></p>
|
||||
</div>
|
||||
<div class="status" id="moneyCol">
|
||||
<img src='project/images/money.png' id="img-money">
|
||||
<img id="img-money">
|
||||
<p class='statusLabel' id='money'></p>
|
||||
</div>
|
||||
<div class="status" id="expCol">
|
||||
<img src='project/images/experience.png' id="img-experience">
|
||||
<img id="img-experience">
|
||||
<p class='statusLabel' id='experience'></p>
|
||||
</div>
|
||||
<div class="status" id="upCol">
|
||||
<img src='project/images/up.png' id="img-up">
|
||||
<img id="img-up">
|
||||
<p class='statusLabel' id='up'></p>
|
||||
</div>
|
||||
<div class="status">
|
||||
@ -94,13 +94,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="toolBar" class="clearfix">
|
||||
<img src="project/images/book.png" class="tools" id='img-book'>
|
||||
<img src="project/images/fly.png" class="tools" id='img-fly'>
|
||||
<img src="project/images/toolbox.png" class="tools" id='img-toolbox'>
|
||||
<img src="project/images/shop.png" class="tools" id='img-shop'>
|
||||
<img src="project/images/save.png" class="tools" id='img-save'>
|
||||
<img src="project/images/load.png" class="tools" id='img-load'>
|
||||
<img src="project/images/settings.png" class="tools" id='img-settings'>
|
||||
<img class="tools" id='img-book'>
|
||||
<img class="tools" id='img-fly'>
|
||||
<img class="tools" id='img-toolbox'>
|
||||
<img class="tools" id='img-shop'>
|
||||
<img class="tools" id='img-save'>
|
||||
<img class="tools" id='img-load'>
|
||||
<img class="tools" id='img-settings'>
|
||||
<p class="statusLabel tools" id="hard"></p>
|
||||
</div>
|
||||
<div id="gif"></div>
|
||||
|
||||
@ -35,11 +35,13 @@ actions.prototype.onkeyUp = function(e) {
|
||||
if (e.keyCode==27) // ESCAPE
|
||||
core.stopReplay();
|
||||
else if (e.keyCode==90) // Z
|
||||
core.rewindReplay();
|
||||
core.speedDownReplay();
|
||||
else if (e.keyCode==88) // X
|
||||
core.forwardReplay();
|
||||
core.speedUpReplay();
|
||||
else if (e.keyCode==32) // SPACE
|
||||
core.triggerReplay();
|
||||
else if (e.keyCode==65) // A
|
||||
core.rewindReplay();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1379,6 +1379,8 @@ control.prototype.startReplay = function (list) {
|
||||
core.status.replay.speed=1.0;
|
||||
core.status.replay.toReplay = core.clone(list);
|
||||
core.status.replay.totalList = core.clone(list);
|
||||
core.status.replay.steps = 0;
|
||||
core.status.replay.save = [];
|
||||
core.updateStatusBar();
|
||||
core.drawTip("开始播放");
|
||||
this.replay();
|
||||
@ -1409,7 +1411,7 @@ control.prototype.resumeReplay = function () {
|
||||
}
|
||||
|
||||
////// 加速播放 //////
|
||||
control.prototype.forwardReplay = function () {
|
||||
control.prototype.speedUpReplay = function () {
|
||||
if (!core.status.replay.replaying) return;
|
||||
core.status.replay.speed = parseInt(10*core.status.replay.speed + 1)/10;
|
||||
if (core.status.replay.speed>3.0) core.status.replay.speed=3.0;
|
||||
@ -1417,7 +1419,7 @@ control.prototype.forwardReplay = function () {
|
||||
}
|
||||
|
||||
////// 减速播放 //////
|
||||
control.prototype.rewindReplay = function () {
|
||||
control.prototype.speedDownReplay = function () {
|
||||
if (!core.status.replay.replaying) return;
|
||||
core.status.replay.speed = parseInt(10*core.status.replay.speed - 1)/10;
|
||||
if (core.status.replay.speed<0.3) core.status.replay.speed=0.3;
|
||||
@ -1432,10 +1434,47 @@ control.prototype.stopReplay = function () {
|
||||
core.status.replay.replaying=false;
|
||||
core.status.replay.pausing=false;
|
||||
core.status.replay.speed=1.0;
|
||||
core.status.replay.steps = 0;
|
||||
core.status.replay.save = [];
|
||||
core.updateStatusBar();
|
||||
core.drawTip("停止播放并恢复游戏");
|
||||
}
|
||||
|
||||
////// 回退 //////
|
||||
control.prototype.rewindReplay = function () {
|
||||
if (!core.status.replay.replaying) return;
|
||||
if (!core.status.replay.pausing) {
|
||||
core.drawTip("请先暂停录像");
|
||||
return;
|
||||
}
|
||||
if (core.status.replay.animate) {
|
||||
core.drawTip("请等待当前事件的处理结束");
|
||||
return;
|
||||
}
|
||||
if (core.status.replay.save.length==0) {
|
||||
core.drawTip("无法再回到上一个节点");
|
||||
return;
|
||||
}
|
||||
|
||||
var save = core.status.replay.save;
|
||||
var data = save.pop();
|
||||
core.loadData(data.data, function () {
|
||||
core.status.replay = {
|
||||
"replaying": true,
|
||||
"pausing": true,
|
||||
"animate": false,
|
||||
"toReplay": data.replay.toReplay,
|
||||
"totalList": data.replay.totalList,
|
||||
"speed": data.replay.speed,
|
||||
"steps": data.replay.steps,
|
||||
"save": save
|
||||
}
|
||||
core.updateStatusBar();
|
||||
core.drawTip("成功回退到上一个节点");
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
////// 回放 //////
|
||||
control.prototype.replay = function () {
|
||||
|
||||
@ -1449,6 +1488,18 @@ control.prototype.replay = function () {
|
||||
return;
|
||||
}
|
||||
|
||||
core.status.replay.steps++;
|
||||
if (core.status.replay.steps%20==0) {
|
||||
if (core.status.replay.save.length == 30)
|
||||
core.status.replay.save.shift();
|
||||
core.status.replay.save.push({"data": core.saveData(), "replay": {
|
||||
"totalList": core.clone(core.status.replay.totalList),
|
||||
"toReplay": core.clone(core.status.replay.toReplay),
|
||||
"speed": core.status.replay.speed,
|
||||
"steps": core.status.replay.steps
|
||||
}});
|
||||
}
|
||||
|
||||
var action=core.status.replay.toReplay.shift();
|
||||
|
||||
if (action=='up' || action=='down' || action=='left' || action=='right') {
|
||||
@ -1696,7 +1747,7 @@ control.prototype.autosave = function (removeLast) {
|
||||
var x=null;
|
||||
if (removeLast)
|
||||
x=core.status.route.pop();
|
||||
core.saveData("autoSave");
|
||||
core.setLocalStorage("autoSave", core.saveData())
|
||||
if (removeLast && core.isset(x))
|
||||
core.status.route.push(x);
|
||||
}
|
||||
@ -1708,7 +1759,7 @@ control.prototype.doSL = function (id, type) {
|
||||
core.drawTip('不能覆盖自动存档!');
|
||||
return;
|
||||
}
|
||||
if (core.saveData("save"+id)) {
|
||||
if (core.setLocalStorage("save"+id, core.saveData())) {
|
||||
core.ui.closePanel();
|
||||
core.drawTip('存档成功!');
|
||||
if (id!="autoSave") {
|
||||
@ -1868,7 +1919,7 @@ control.prototype.syncLoad = function () {
|
||||
}
|
||||
|
||||
////// 存档到本地 //////
|
||||
control.prototype.saveData = function(dataId) {
|
||||
control.prototype.saveData = function() {
|
||||
var data = {
|
||||
'floorId': core.status.floorId,
|
||||
'hero': core.clone(core.status.hero),
|
||||
@ -1888,7 +1939,7 @@ control.prototype.saveData = function(dataId) {
|
||||
}
|
||||
core.events.beforeSaveData(data);
|
||||
|
||||
return core.setLocalStorage(dataId, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
////// 从本地读档 //////
|
||||
@ -2133,15 +2184,15 @@ control.prototype.updateStatusBar = function () {
|
||||
core.statusBar.image.fly.src = core.statusBar.icons.stop.src;
|
||||
core.statusBar.image.fly.style.opacity = 1;
|
||||
|
||||
//core.statusBar.image.toolbox.src = core.statusBar.icons.forward.src;
|
||||
core.statusBar.image.toolbox.style.opacity = 0;
|
||||
core.statusBar.image.toolbox.src = core.statusBar.icons.rewind.src;
|
||||
core.statusBar.image.toolbox.style.opacity = 1;
|
||||
|
||||
core.statusBar.image.shop.style.opacity = 0;
|
||||
|
||||
core.statusBar.image.save.src = core.statusBar.icons.rewind.src;
|
||||
core.statusBar.image.save.src = core.statusBar.icons.speedDown.src;
|
||||
core.statusBar.image.save.style.opacity = 1;
|
||||
|
||||
core.statusBar.image.load.src = core.statusBar.icons.forward.src;
|
||||
core.statusBar.image.load.src = core.statusBar.icons.speedUp.src;
|
||||
core.statusBar.image.load.style.opacity = 1;
|
||||
|
||||
core.statusBar.image.settings.style.opacity = 0;
|
||||
|
||||
23
libs/core.js
@ -114,7 +114,9 @@ function core() {
|
||||
'animate': false, // 正在某段动画中
|
||||
'toReplay': [],
|
||||
'totalList': [],
|
||||
'speed': 1.0
|
||||
'speed': 1.0,
|
||||
'steps': 0,
|
||||
'save': [],
|
||||
},
|
||||
|
||||
// event事件
|
||||
@ -252,7 +254,6 @@ core.prototype.init = function (coreData, callback) {
|
||||
core.material.ground = new Image();
|
||||
core.material.ground.src = "project/images/ground.png";
|
||||
|
||||
|
||||
core.loader.load(function () {
|
||||
console.log(core.material);
|
||||
// 设置勇士高度
|
||||
@ -842,6 +843,11 @@ core.prototype.clone = function (data) {
|
||||
return core.utils.clone(data);
|
||||
}
|
||||
|
||||
////// 裁剪图片 //////
|
||||
core.prototype.cropImage = function (image, size) {
|
||||
return core.utils.cropImage(image, size);
|
||||
}
|
||||
|
||||
////// 格式化时间为字符串 //////
|
||||
core.prototype.formatDate = function(date) {
|
||||
return core.utils.formatDate(date);
|
||||
@ -893,11 +899,16 @@ core.prototype.resumeReplay = function () {
|
||||
}
|
||||
|
||||
////// 加速播放 //////
|
||||
core.prototype.forwardReplay = function () {
|
||||
core.control.forwardReplay();
|
||||
core.prototype.speedUpReplay = function () {
|
||||
core.control.speedUpReplay();
|
||||
}
|
||||
|
||||
////// 减速播放 //////
|
||||
core.prototype.speedDownReplay = function () {
|
||||
core.control.speedDownReplay();
|
||||
}
|
||||
|
||||
////// 回退播放 //////
|
||||
core.prototype.rewindReplay = function () {
|
||||
core.control.rewindReplay();
|
||||
}
|
||||
@ -973,8 +984,8 @@ core.prototype.syncLoad = function () {
|
||||
}
|
||||
|
||||
////// 存档到本地 //////
|
||||
core.prototype.saveData = function(dataId) {
|
||||
return core.control.saveData(dataId);
|
||||
core.prototype.saveData = function() {
|
||||
return core.control.saveData();
|
||||
}
|
||||
|
||||
////// 从本地读档 //////
|
||||
|
||||
@ -123,6 +123,16 @@ events.prototype.lose = function (reason) {
|
||||
////// 游戏结束 //////
|
||||
events.prototype.gameOver = function (ending, fromReplay) {
|
||||
|
||||
// 清空图片和天气
|
||||
core.clearMap('animate', 0, 0, 416, 416);
|
||||
while (core.dom.gif2.firstChild)
|
||||
core.dom.gif2.removeChild(core.dom.gif2.firstChild);
|
||||
core.clearMap('weather', 0, 0, 416, 416)
|
||||
core.animateFrame.weather.type = null;
|
||||
core.animateFrame.weather.level = 0;
|
||||
core.animateFrame.weather.nodes = [];
|
||||
core.setFg(null, 0);
|
||||
|
||||
// 下载录像
|
||||
var confirmDownload = function () {
|
||||
core.ui.closePanel();
|
||||
|
||||
@ -23,6 +23,9 @@ loader.prototype.setStartLoadTipText = function (text) {
|
||||
|
||||
loader.prototype.load = function (callback) {
|
||||
|
||||
// 加载icons
|
||||
core.loader.loadIcons();
|
||||
|
||||
// 加载图片
|
||||
core.loader.loadImages(core.materials, core.material.images, function () {
|
||||
// 加载png图片
|
||||
@ -40,6 +43,20 @@ loader.prototype.load = function (callback) {
|
||||
})
|
||||
}
|
||||
|
||||
loader.prototype.loadIcons = function () {
|
||||
|
||||
this.loadImage("icons.png", function (id, image) {
|
||||
var images = core.cropImage(image);
|
||||
for (var key in core.statusBar.icons) {
|
||||
if (typeof core.statusBar.icons[key] == 'number') {
|
||||
core.statusBar.icons[key] = images[core.statusBar.icons[key]];
|
||||
if (core.isset(core.statusBar.image[key]))
|
||||
core.statusBar.image[key].src = core.statusBar.icons[key].src;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loader.prototype.loadImages = function (names, toSave, callback) {
|
||||
if (names.length==0) {
|
||||
if (core.isset(callback)) callback();
|
||||
|
||||
@ -20,6 +20,8 @@ ui.prototype.clearMap = function (map, x, y, width, height) {
|
||||
for (var m in core.canvas) {
|
||||
core.canvas[m].clearRect(0, 0, 416, 416);
|
||||
}
|
||||
while (core.dom.gif.firstChild)
|
||||
core.dom.gif.removeChild(core.dom.gif.firstChild);
|
||||
}
|
||||
else {
|
||||
core.canvas[map].clearRect(x||0, y||0, width||416, height||416);
|
||||
|
||||
@ -126,6 +126,24 @@ utils.prototype.clone = function (data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
////// 裁剪图片 //////
|
||||
utils.prototype.cropImage = function (image, size) {
|
||||
size = size||32;
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
var ans = [];
|
||||
for (var i=0;i<image.height;i+=size) {
|
||||
context.drawImage(image, 0, i, size, size, 0, 0, size, size);
|
||||
var img = new Image();
|
||||
img.src = canvas.toDataURL("image/png");
|
||||
ans.push(img);
|
||||
context.clearRect(0,0,size,size);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
////// 格式化时间为字符串 //////
|
||||
utils.prototype.formatDate = function(date) {
|
||||
if (!core.isset(date)) return "";
|
||||
|
||||
50
main.js
@ -88,17 +88,29 @@ function main() {
|
||||
'settings': document.getElementById("img-settings")
|
||||
},
|
||||
'icons': {
|
||||
'book': null,
|
||||
'fly': null,
|
||||
'toolbox': null,
|
||||
'save': null,
|
||||
'load': null,
|
||||
'settings': null,
|
||||
'rewind': null, // 减速
|
||||
'forward': null, // 加速
|
||||
'play': null, // 播放
|
||||
'pause': null, // 暂停
|
||||
'stop': null, // 停止
|
||||
'floor': 0,
|
||||
'lv': 1,
|
||||
'hpmax': 2,
|
||||
'hp': 3,
|
||||
'atk': 4,
|
||||
'def': 5,
|
||||
'mdef': 6,
|
||||
'money': 7,
|
||||
'experience': 8,
|
||||
'up': 9,
|
||||
'book': 10,
|
||||
'fly': 11,
|
||||
'toolbox': 12,
|
||||
'shop': 13,
|
||||
'save': 14,
|
||||
'load': 15,
|
||||
'settings': 16,
|
||||
'play': 17,
|
||||
'pause': 18,
|
||||
'stop': 19,
|
||||
'speedDown': 20,
|
||||
'speedUp': 21,
|
||||
'rewind': 22,
|
||||
},
|
||||
'floor': document.getElementById('floor'),
|
||||
'lv': document.getElementById('lv'),
|
||||
@ -130,11 +142,7 @@ main.prototype.init = function (mode, callback) {
|
||||
main.mode = mode;
|
||||
if (mode === 'editor')main.editor = {'disableGlobalAnimate':true};
|
||||
}
|
||||
Object.keys(this.statusBar.icons).forEach(function (t) {
|
||||
var image=new Image();
|
||||
image.src="project/images/"+t+".png";
|
||||
main.statusBar.icons[t] = image;
|
||||
})
|
||||
|
||||
main.loaderJs('project', main.pureData, function(){
|
||||
var mainData = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main;
|
||||
for(var ii in mainData)main[ii]=mainData[ii];
|
||||
@ -383,6 +391,12 @@ main.statusBar.image.fly.onclick = function () {
|
||||
|
||||
////// 点击状态栏中的工具箱时 //////
|
||||
main.statusBar.image.toolbox.onclick = function () {
|
||||
|
||||
if (core.isset(core.status.replay) && core.status.replay.replaying) {
|
||||
core.rewindReplay();
|
||||
return;
|
||||
}
|
||||
|
||||
if (main.core.isPlaying())
|
||||
main.core.openToolbox(true);
|
||||
}
|
||||
@ -397,7 +411,7 @@ main.statusBar.image.shop.onclick = function () {
|
||||
main.statusBar.image.save.onclick = function () {
|
||||
|
||||
if (core.isset(core.status.replay) && core.status.replay.replaying) {
|
||||
core.rewindReplay();
|
||||
core.speedDownReplay();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -409,7 +423,7 @@ main.statusBar.image.save.onclick = function () {
|
||||
main.statusBar.image.load.onclick = function () {
|
||||
|
||||
if (core.isset(core.status.replay) && core.status.replay.replaying) {
|
||||
core.forwardReplay();
|
||||
core.speedUpReplay();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -18,8 +18,8 @@ main.floors.sample1 =
|
||||
[ 7,121, 8,152, 9,120, 10,352,176,175,122,175,176],
|
||||
[ 0, 0, 0, 0, 0, 0, 0,352,175,174, 0,172,175],
|
||||
[352,352,352,352,111,352,352,352, 0, 0,229, 0, 0],
|
||||
[ 43, 33, 44,151, 0, 0, 0,352,175,171, 0,173,175],
|
||||
[ 21, 22, 21,151, 0, 0, 0,352,176,175, 0,175,176],
|
||||
[ 43, 33, 44,351, 0, 0, 0,352,175,171, 0,173,175],
|
||||
[ 21, 22, 21,351, 0, 0, 0,352,176,175, 0,175,176],
|
||||
[351,245,351,351, 0, 87, 0,352,352,352, 85,353,353],
|
||||
[ 0,246, 0,351, 0, 0, 0,352,152,221, 0,221,353],
|
||||
[246, 0,246,351, 0, 0, 0,111, 85, 0, 0, 0,353],
|
||||
|
||||
|
Before Width: | Height: | Size: 207 B |
|
Before Width: | Height: | Size: 464 B |
|
Before Width: | Height: | Size: 276 B |
|
Before Width: | Height: | Size: 508 B |
|
Before Width: | Height: | Size: 995 B |
|
Before Width: | Height: | Size: 854 B |
|
Before Width: | Height: | Size: 662 B |
|
Before Width: | Height: | Size: 479 B |
|
Before Width: | Height: | Size: 481 B |
BIN
project/images/icons.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 478 B |
|
Before Width: | Height: | Size: 291 B |
|
Before Width: | Height: | Size: 269 B |
|
Before Width: | Height: | Size: 340 B |
|
Before Width: | Height: | Size: 689 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 239 B |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 863 B |
10
更新说明.txt
@ -1,16 +1,16 @@
|
||||
HTML5魔塔样板V2.0.2
|
||||
HTML5魔塔样板V2.1
|
||||
|
||||
编辑器添加新建和删除按钮;地图自动保存 √
|
||||
录像支持倒退(录像播放中每50步自动存档,最多存20个)
|
||||
录像支持倒退(录像播放中每20步自动存档,最多存30个) √
|
||||
Gif支持:可以作为楼层背景图或者使用显示动图事件 √
|
||||
图片显示增加淡入淡出效果 √
|
||||
APP端也能下载录像
|
||||
APP端也能下载或读取文件 √
|
||||
地图临界显伤 √
|
||||
单个存档清理 √
|
||||
大数据魔塔的支持(临界计算等) √
|
||||
进一步对JS文件进行压缩 √
|
||||
进一步对JS文件和图标进行压缩 √
|
||||
修复有时候无法输入ID的问题 √
|
||||
其他细节优化
|
||||
其他细节优化 √
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
|
||||