mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-11-04 15:12:58 +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