diff --git a/script/rdview2.js b/script/rdview2.js index b8ac205..a3f2c52 100644 --- a/script/rdview2.js +++ b/script/rdview2.js @@ -2,11 +2,16 @@ import { Extensions, IconStyle } from "/script/Extensions.js"; function rend() { const codeBlocks = document.querySelectorAll("canvas.rdview2-canvas"); codeBlocks.forEach((block) => { + // 清理旧的实例 + if (block.cleanup) { + block.cleanup(); + } + const width = block.getAttribute("data-width") || 8; const height = block.getAttribute("data-height") || 4; const scale = block.getAttribute("data-scale") || 2; const data = block.getAttribute("data"); - const ctx = block.getContext("2d"); + const ctx = block.getContext("2d", { alpha: false, desynchronized: true }); block.width = width * scale * 14; block.height = height * scale * 14; if (!data) { @@ -14,34 +19,68 @@ function rend() { } const jsonData = JSON.parse(`[${JSON.parse(data)}]`); - let lastX = -1, lastY = -1, lastActive = false; + let lastActive = false; + let evtId = 0; let frameId = null; + let cachedRect = null; + let mdown = false; + + const eventPositions = jsonData.map(evt => ({ + evt, + x: ((evt.beat ?? 1) - 1) * scale * 14, + y: (evt.y ?? 0) * scale * 14 + })); const style = new IconStyle(); + style.Scale = scale; + + const updateRect = () => { + cachedRect = block.getBoundingClientRect(); + }; + updateRect(); + + let rectTimeout; + const debouncedUpdateRect = () => { + clearTimeout(rectTimeout); + rectTimeout = setTimeout(updateRect, 100); + }; + + const observer = new ResizeObserver(debouncedUpdateRect); + observer.observe(block); + + window.addEventListener("resize", debouncedUpdateRect, { passive: true }); + window.addEventListener("scroll", debouncedUpdateRect, { passive: true }); block.render = (x, y, active) => { - if (x === lastX && y === lastY && active === lastActive) { + const point = { x, y }; + let currentEvtId = null; + for (let i = 0; i < eventPositions.length; i++) { + const { evt } = eventPositions[i]; + if (evt.area?.contains(point)) { + currentEvtId = i; + break; + } + } + + if (currentEvtId === evtId && active === lastActive) { return; } - lastX = x; - lastY = y; + evtId = currentEvtId; lastActive = active; ctx.clearRect(0, 0, block.width, block.height); - for (const evt of jsonData) { - style.Scale = scale; - style.Hover = evt.area?.contains({x, y}) ?? false; - style.Active = evt.area?.contains({x, y}) ? active : false; - evt.area = Extensions.DrawEventIcon(ctx, evt, { - x: ((evt.beat ?? 1) - 1) * scale * 14, - y: (evt.y ?? 0) * scale * 14, - }, style); + + for (const { evt, x: posX, y: posY } of eventPositions) { + const isHovered = evt.area?.contains(point) ?? false; + style.Hover = isHovered; + style.Active = isHovered ? active : false; + evt.area = Extensions.DrawEventIcon(ctx, evt, { x: posX, y: posY }, style); } } const throttledRender = (x, y, active) => { if (frameId !== null) { - cancelAnimationFrame(frameId); + return; } frameId = requestAnimationFrame(() => { block.render(x, y, active); @@ -49,31 +88,51 @@ function rend() { }); } + const getMousePos = (e) => ({ + x: e.clientX - cachedRect.left, + y: e.clientY - cachedRect.top + }); + block.addEventListener("mousemove", (e) => { - const rect = block.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - throttledRender(x, y, false); - }); + const { x, y } = getMousePos(e); + console.log(x, y); + throttledRender(x, y, mdown); + }, { passive: true }); + block.addEventListener("mousedown", (e) => { - const rect = block.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - block.render(x, y, true); + const { x, y } = getMousePos(e); + mdown = true; + block.render(x, y, mdown); }); + block.addEventListener("mouseup", (e) => { - const rect = block.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - block.render(x, y, false); + const { x, y } = getMousePos(e); + mdown = false; + block.render(x, y, mdown); }); - block.addEventListener("mouseleave", (e) => { + + block.addEventListener("mouseleave", () => { + if (frameId !== null) { + cancelAnimationFrame(frameId); + frameId = null; + } + mdown = false; + evtId = null; + lastActive = false; + block.render(-1, -1, mdown); + }, { passive: true }); + + block.cleanup = () => { + observer.disconnect(); + window.removeEventListener("resize", debouncedUpdateRect); + window.removeEventListener("scroll", debouncedUpdateRect); + clearTimeout(rectTimeout); if (frameId !== null) { cancelAnimationFrame(frameId); } - block.render(0, 0, false); - }); - block.render(0, 0, false); + }; + + block.render(-1, -1, mdown); }); }