mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-31 23:29:27 +08:00
feat: 弹幕性能优化
This commit is contained in:
parent
6202dbce62
commit
f2fecbb6c6
5
idea.md
5
idea.md
@ -23,8 +23,9 @@
|
|||||||
|
|
||||||
[] 苍蓝之殿 1: 红蓝黄门转换
|
[] 苍蓝之殿 1: 红蓝黄门转换
|
||||||
[] 苍蓝之殿 2:
|
[] 苍蓝之殿 2:
|
||||||
[] 苍蓝之殿 3:
|
[] 苍蓝之殿 3: 传送门
|
||||||
[] 苍蓝之殿 4:
|
[] 苍蓝之殿 4:
|
||||||
|
[] 苍蓝之殿中: 让我们把这些东西结合起来...
|
||||||
|
|
||||||
### 成就
|
### 成就
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ dam4.png ---- 存档 59
|
|||||||
[] 重构技能树结构
|
[] 重构技能树结构
|
||||||
[] 技能树允许自动升级
|
[] 技能树允许自动升级
|
||||||
[] 重构装备系统
|
[] 重构装备系统
|
||||||
[] 弹幕系统
|
[x] 弹幕系统
|
||||||
[x] 优化各种 ui
|
[x] 优化各种 ui
|
||||||
[] 怪物脚下加入阴影
|
[] 怪物脚下加入阴影
|
||||||
[x] 着色器特效
|
[x] 着色器特效
|
||||||
|
@ -109,7 +109,7 @@ export class Danmaku extends EventEmitter<DanmakuEvent> {
|
|||||||
strokeColor: string = 'black';
|
strokeColor: string = 'black';
|
||||||
|
|
||||||
private posted: boolean = false;
|
private posted: boolean = false;
|
||||||
private vNode?: VNode;
|
vNode?: VNode;
|
||||||
private posting: boolean = false;
|
private posting: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -351,6 +351,7 @@ export class Danmaku extends EventEmitter<DanmakuEvent> {
|
|||||||
* 显示这个弹幕
|
* 显示这个弹幕
|
||||||
*/
|
*/
|
||||||
show() {
|
show() {
|
||||||
|
if (this.showing) return;
|
||||||
this.showing = true;
|
this.showing = true;
|
||||||
Danmaku.showList.push(this);
|
Danmaku.showList.push(this);
|
||||||
Danmaku.showMap.set(this.id, this);
|
Danmaku.showMap.set(this.id, this);
|
||||||
@ -361,6 +362,7 @@ export class Danmaku extends EventEmitter<DanmakuEvent> {
|
|||||||
* 显示结束这个弹幕
|
* 显示结束这个弹幕
|
||||||
*/
|
*/
|
||||||
showEnd() {
|
showEnd() {
|
||||||
|
if (!this.showing) return;
|
||||||
this.showing = false;
|
this.showing = false;
|
||||||
deleteWith(Danmaku.showList, this);
|
deleteWith(Danmaku.showList, this);
|
||||||
Danmaku.showMap.delete(this.id);
|
Danmaku.showMap.delete(this.id);
|
||||||
|
@ -31,19 +31,16 @@ import { mainSetting } from '@/core/main/setting';
|
|||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
|
|
||||||
interface ElementMap {
|
interface ElementMap {
|
||||||
pos: number;
|
|
||||||
ele: HTMLDivElement;
|
ele: HTMLDivElement;
|
||||||
danmaku: Danmaku;
|
danmaku: Danmaku;
|
||||||
style: CSSStyleDeclaration;
|
|
||||||
width: number;
|
|
||||||
hover: boolean;
|
hover: boolean;
|
||||||
top: number;
|
top: number;
|
||||||
|
style: CSSStyleDeclaration;
|
||||||
}
|
}
|
||||||
|
|
||||||
const map = Danmaku.showMap;
|
const map = Danmaku.showMap;
|
||||||
const eleMap: Map<number, ElementMap> = new Map();
|
const eleMap: Map<number, ElementMap> = new Map();
|
||||||
const liked = reactive<Record<number, boolean>>({});
|
const liked = reactive<Record<number, boolean>>({});
|
||||||
const ticker = new Ticker();
|
|
||||||
|
|
||||||
const speed = mainSetting.getValue('ui.danmakuSpeed', 60);
|
const speed = mainSetting.getValue('ui.danmakuSpeed', 60);
|
||||||
|
|
||||||
@ -53,10 +50,10 @@ const likeFn = (l: boolean, d: Danmaku) => {
|
|||||||
|
|
||||||
watch(Danmaku.showList, list => {
|
watch(Danmaku.showList, list => {
|
||||||
list.forEach(v => {
|
list.forEach(v => {
|
||||||
liked[v.id] = v.liked;
|
|
||||||
v.on('like', likeFn);
|
|
||||||
if (!eleMap.has(v.id)) {
|
if (!eleMap.has(v.id)) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
liked[v.id] = v.liked;
|
||||||
|
v.on('like', likeFn);
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
addElement(v.id);
|
addElement(v.id);
|
||||||
});
|
});
|
||||||
@ -71,59 +68,39 @@ function addElement(id: number) {
|
|||||||
const div = document.getElementById(`danmaku-${id}`);
|
const div = document.getElementById(`danmaku-${id}`);
|
||||||
if (!div) return;
|
if (!div) return;
|
||||||
|
|
||||||
const style = getComputedStyle(div);
|
|
||||||
|
|
||||||
const ele: ElementMap = {
|
const ele: ElementMap = {
|
||||||
danmaku,
|
danmaku,
|
||||||
pos: window.innerWidth + 100,
|
|
||||||
ele: div as HTMLDivElement,
|
ele: div as HTMLDivElement,
|
||||||
style,
|
|
||||||
width: parseInt(style.width),
|
|
||||||
hover: false,
|
hover: false,
|
||||||
top: -1
|
top: -1,
|
||||||
|
style: getComputedStyle(div)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
div.style.setProperty('--end', `${-div.scrollWidth}px`);
|
||||||
|
div.style.setProperty(
|
||||||
|
'--duration',
|
||||||
|
`${Math.floor((window.innerWidth + div.scrollWidth) / speed)}s`
|
||||||
|
);
|
||||||
|
div.style.setProperty('left', ele.style.left);
|
||||||
|
div.addEventListener('animationend', () => {
|
||||||
|
danmaku.showEnd();
|
||||||
|
eleMap.delete(id);
|
||||||
|
});
|
||||||
|
|
||||||
eleMap.set(id, ele);
|
eleMap.set(id, ele);
|
||||||
calTop(id);
|
calTop(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastTime = 0;
|
|
||||||
ticker.add(time => {
|
|
||||||
const dt = (time - lastTime) / 1000;
|
|
||||||
const toDelete: number[] = [];
|
|
||||||
|
|
||||||
eleMap.forEach((value, id) => {
|
|
||||||
const { danmaku, ele, style, width, hover } = value;
|
|
||||||
if (!hover) {
|
|
||||||
const dx = dt * speed;
|
|
||||||
value.pos -= dx;
|
|
||||||
ele.style.transform = `translateX(${value.pos.toFixed(2)}px)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.pos + width < 0) {
|
|
||||||
toDelete.push(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lastTime = time;
|
|
||||||
|
|
||||||
toDelete.forEach(v => {
|
|
||||||
eleMap.delete(v);
|
|
||||||
const danmaku = map.get(v);
|
|
||||||
if (danmaku) {
|
|
||||||
danmaku.showEnd();
|
|
||||||
}
|
|
||||||
map.delete(v);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function mousein(id: number) {
|
function mousein(id: number) {
|
||||||
const danmaku = eleMap.get(id)!;
|
const danmaku = eleMap.get(id)!;
|
||||||
danmaku.hover = true;
|
danmaku.hover = true;
|
||||||
|
danmaku.ele.classList.add('danmaku-paused');
|
||||||
}
|
}
|
||||||
|
|
||||||
function mouseleave(id: number) {
|
function mouseleave(id: number) {
|
||||||
const danmaku = eleMap.get(id)!;
|
const danmaku = eleMap.get(id)!;
|
||||||
danmaku.hover = false;
|
danmaku.hover = false;
|
||||||
|
danmaku.ele.classList.remove('danmaku-paused');
|
||||||
}
|
}
|
||||||
|
|
||||||
const touchDebounce = debounce(mouseleave, 3000);
|
const touchDebounce = debounce(mouseleave, 3000);
|
||||||
@ -138,15 +115,19 @@ function calTop(id: number) {
|
|||||||
|
|
||||||
const used: Set<number> = new Set();
|
const used: Set<number> = new Set();
|
||||||
eleMap.forEach(v => {
|
eleMap.forEach(v => {
|
||||||
const { pos, width } = v;
|
const { ele, style } = v;
|
||||||
|
const pos = parseInt(style.transform.slice(19, -4));
|
||||||
|
const width = ele.scrollWidth;
|
||||||
if (
|
if (
|
||||||
pos <= window.innerWidth + 125 &&
|
pos <= window.innerWidth + 200 &&
|
||||||
pos + width >= window.innerWidth + 75
|
pos + width >= window.innerWidth
|
||||||
) {
|
) {
|
||||||
used.add(v.top);
|
used.add(v.top);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let i = -1;
|
let i = -1;
|
||||||
|
danmaku.top = 0;
|
||||||
|
danmaku.ele.style.top = `20px`;
|
||||||
while (++i < 20) {
|
while (++i < 20) {
|
||||||
if (!used.has(i)) {
|
if (!used.has(i)) {
|
||||||
danmaku.top = i;
|
danmaku.top = i;
|
||||||
@ -156,9 +137,7 @@ function calTop(id: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {});
|
||||||
ticker.destroy();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@ -176,6 +155,8 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.danmaku-one {
|
.danmaku-one {
|
||||||
|
--end: 0;
|
||||||
|
--duration: 5s;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
transform: translateX(100vw);
|
transform: translateX(100vw);
|
||||||
@ -185,11 +166,27 @@ onUnmounted(() => {
|
|||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
animation: danmaku-roll linear var(--duration) forwards;
|
||||||
|
animation-play-state: running;
|
||||||
}
|
}
|
||||||
|
|
||||||
.danmaku-one:hover {
|
.danmaku-one:hover {
|
||||||
background-color: #0006;
|
background-color: #0006;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
.danmaku-one .danmaku-paused {
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes danmaku-roll {
|
||||||
|
0% {
|
||||||
|
transform: translateX(100vw);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(var(--end));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.danmaku-info {
|
.danmaku-info {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
id="danmaku-input-input"
|
id="danmaku-input-input"
|
||||||
:max-length="200"
|
:max-length="200"
|
||||||
v-model:value="inputValue"
|
v-model:value="inputValue"
|
||||||
placeholder="请在此输入弹幕"
|
placeholder="请在此输入弹幕,显示中括号请使用\[或\]"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@change="input(inputValue)"
|
@change="input(inputValue)"
|
||||||
@pressEnter="inputEnter()"
|
@pressEnter="inputEnter()"
|
||||||
@ -302,7 +302,20 @@ function input(value: string) {
|
|||||||
if (size > 200) {
|
if (size > 200) {
|
||||||
tip('warning', '弹幕长度超限!');
|
tip('warning', '弹幕长度超限!');
|
||||||
}
|
}
|
||||||
danmaku.text = value;
|
|
||||||
|
const before = danmaku.text;
|
||||||
|
const { info, ret } = logger.catch(() => {
|
||||||
|
danmaku.text = value;
|
||||||
|
return danmaku.parse();
|
||||||
|
});
|
||||||
|
if (info.length > 0) {
|
||||||
|
if (info[0].code === 4) {
|
||||||
|
tip('error', '请检查中括号匹配');
|
||||||
|
danmaku.text = before;
|
||||||
|
} else {
|
||||||
|
danmaku.vNode = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function inputEnter() {
|
function inputEnter() {
|
||||||
|
Loading…
Reference in New Issue
Block a user