From 9498781bd0ed9d767bbd4a9c92f8bdd550c4a9e8 Mon Sep 17 00:00:00 2001 From: Monado Toolman Date: Fri, 1 Nov 2024 19:57:58 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8canvas=E5=AE=9E=E7=8E=B0cd=E5=AD=90?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mastodon/features/compose/index.js | 110 ++++++++---------- .../ui/components/cdko_halloween_drawer.js | 103 ++++++++++++++++ app/javascript/styles/ffxiv/drawer_cdko.scss | 15 --- .../styles/mastodon/components.scss | 17 +-- 4 files changed, 156 insertions(+), 89 deletions(-) create mode 100644 app/javascript/mastodon/features/ui/components/cdko_halloween_drawer.js delete mode 100644 app/javascript/styles/ffxiv/drawer_cdko.scss diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index a58ae8847e5774..d1539c2e8addcf 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -18,6 +18,8 @@ import { mascot } from '../../initial_state'; import Icon from 'mastodon/components/icon'; import { logOut } from 'mastodon/utils/log_out'; +import CDkoHalloweenDrawerCanvasComponent from '../ui/components/cdko_halloween_drawer'; + const messages = defineMessages({ start: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' }, @@ -49,7 +51,7 @@ class Compose extends React.PureComponent { intl: PropTypes.object.isRequired, }; - componentDidMount() { + componentDidMount () { const { isSearchPage } = this.props; if (!isSearchPage) { @@ -57,7 +59,7 @@ class Compose extends React.PureComponent { } } - componentWillUnmount() { + componentWillUnmount () { const { isSearchPage } = this.props; if (!isSearchPage) { @@ -89,73 +91,61 @@ class Compose extends React.PureComponent { this.props.dispatch(changeComposing(false)); } - render() { + render () { const { multiColumn, showSearch, isSearchPage, intl } = this.props; let header = ''; - const getVideoSrc = () => { - if (document.body.classList.contains('theme-ffxiv')) { - return 'https://media.monado.ren/site_uploads/241028/f.mp4'; - } else if (document.body.classList.contains('theme-switch-black')) { - return 'https://media.monado.ren/site_uploads/241028/b.mp4'; - } else if (document.body.classList.contains('theme-default') || document.body.classList.contains('theme-contrast')) { - return 'https://media.monado.ren/site_uploads/241028/m.mp4'; - } else if (document.body.classList.contains('theme-mastodon-light') || document.body.classList.contains('theme-switch-white') || document.body.classList.contains('theme-nintendo-red')) { - return 'https://media.monado.ren/site_uploads/241028/w.mp4'; - } + + if (multiColumn) { + const { columns } = this.props; + header = ( + + ); } - const videoSrc = getVideoSrc(); - if (multiColumn) { - const { columns } = this.props; - header = ( - - ); - } - return ( -
- {header} + return ( +
+ {header} - {(multiColumn || isSearchPage) && } + {(multiColumn || isSearchPage) && } -
- {!isSearchPage &&
- +
+ {!isSearchPage &&
+ - + -
- - {videoSrc && (
} + + + {({ x }) => ( +
+
-
} - - - {({ x }) => ( -
- -
- )} -
-
+ )} +
- ); - } - +
+ ); } + +} diff --git a/app/javascript/mastodon/features/ui/components/cdko_halloween_drawer.js b/app/javascript/mastodon/features/ui/components/cdko_halloween_drawer.js new file mode 100644 index 00000000000000..83100def8b9c4c --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/cdko_halloween_drawer.js @@ -0,0 +1,103 @@ +import React from 'react'; + +class CDkoHalloweenDrawerCanvasComponent extends React.Component { + constructor(props) { + super(props); + this.canvasRef = React.createRef(); + } + + componentDidMount() { + const canvas = this.canvasRef.current; + const ctx = canvas.getContext('2d'); + + const img = new Image(); + img.src = 'https://media.monado.ren/site_uploads/241028/c6c7ed8ee0c2dac51b282ff928a8e6a2.png'; + + const eyeWidth = 275; + const eyeHeight = 105; + const cdkoWidth = 1377; + const cdkoHeight = 1350; + const silkieWidth = 171; + const silkieHeight = 439; + const ghostWidth = 176; + const ghostHeight = 312; + + img.onload = () => { + // 裁剪并放置图像 + ctx.drawImage(img, 0, 0, cdkoWidth, cdkoHeight, 0, 0, cdkoWidth, cdkoHeight); // cdko 图 + ctx.drawImage(img, 1377, 839, ghostWidth, ghostHeight, 1085, 567, ghostWidth, ghostHeight); // ghost 图 + ctx.drawImage(img, 1377, 0, silkieWidth, silkieHeight, 424, 91, silkieWidth, silkieHeight); // silkie 图 + ctx.drawImage(img, 1553, 0, eyeWidth, eyeHeight, 740, 498, eyeWidth, eyeHeight); // 初始eye_a 图 + + // 示例帧数据,假设有 10 帧 + const eyesFrames = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 2, 2, 2, 1, 1, 3, 3, 6, 6, 6, 6, 3, 1, 2, 2, 2, 0, 0, 2, 2, 1, 1, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 3, 1, 1, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 1, 3, 3, 6, 6, 6, 3, 1, 4, 0, 0, 0, 0, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 1, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 3, 1, 1, 2, 2, 0, 0, 0]; + + const ghostFrames = [[567, 255], [566, 255], [565, 254], [564, 253], [562, 252], [559, 251], [557, 249], [553, 247], [550, 245], [546, 243], [541, 240], [537, 238], [532, 235], [527, 232], [522, 230], [517, 227], [511, 224], [506, 221], [500, 219], [495, 216], [489, 213], [484, 211], [479, 209], [474, 206], [469, 205], [465, 203], [460, 201], [456, 200], [453, 199], [449, 199], [447, 199], [444, 199], [441, 199], [438, 200], [435, 201], [431, 203], [428, 205], [425, 206], [421, 209], [417, 211], [414, 213], [410, 216], [407, 219], [403, 221], [400, 224], [397, 227], [393, 230], [390, 232], [387, 235], [384, 238], [381, 240], [379, 243], [376, 245], [374, 247], [372, 249], [371, 251], [369, 252], [368, 253], [367, 254], [367, 255], [367, 255], [367, 255], [368, 254], [369, 253], [370, 252], [372, 251], [375, 249], [378, 247], [381, 245], [384, 243], [387, 240], [391, 238], [395, 235], [399, 232], [403, 230], [407, 227], [411, 224], [414, 221], [418, 219], [422, 216], [426, 213], [429, 211], [432, 209], [435, 206], [438, 205], [441, 203], [443, 201], [444, 200], [446, 199], [446, 199], [447, 199], [446, 199], [446, 199], [444, 200], [443, 201], [441, 203], [438, 205], [435, 206], [432, 209], [429, 211], [426, 213], [422, 216], [418, 219], [414, 221], [411, 224], [407, 227], [403, 230], [399, 232], [395, 235], [391, 238], [387, 240], [384, 243], [381, 245], [378, 247], [375, 249], [372, 251], [370, 252], [369, 253], [368, 254], [367, 255], [367, 255], [367, 255], [367, 254], [368, 253], [369, 252], [371, 251], [372, 249], [374, 247], [376, 245], [379, 243], [381, 240], [384, 238], [387, 235], [390, 232], [393, 230], [397, 227], [400, 224], [403, 221], [407, 219], [410, 216], [414, 213], [417, 211], [421, 209], [425, 206], [428, 205], [431, 203], [435, 201], [438, 200], [441, 199], [444, 199], [447, 199], [449, 199], [453, 199], [456, 200], [460, 201], [465, 203], [469, 205], [474, 206], [479, 209], [484, 211], [489, 213], [495, 216], [500, 219], [506, 221], [511, 224], [517, 227], [522, 230], [527, 232], [532, 235], [537, 238], [541, 240], [546, 243], [550, 245], [553, 247], [557, 249], [559, 251], [562, 252], [564, 253], [565, 254], [566, 255], [567, 255]]; + + const silkieFrames = [91, 91, 92, 93, 95, 97, 100, 103, 107, 110, 115, 119, 124, 129, 135, 140, 146, 152, 158, 165, 171, 177, 184, 191, 197, 204, 210, 216, 223, 229, 235, 241, 246, 252, 257, 262, 266, 271, 274, 278, 281, 284, 286, 288, 289, 290, 291, 290, 290, 288, 287, 285, 283, 280, 277, 273, 270, 266, 261, 257, 252, 247, 242, 237, 231, 226, 220, 214, 208, 202, 197, 191, 185, 179, 173, 167, 161, 155, 150, 144, 139, 134, 129, 124, 120, 115, 111, 108, 104, 101, 98, 96, 94, 93, 91, 91, 91, 91, 92, 93, 95, 97, 100, 103, 107, 110, 115, 119, 124, 129, 135, 140, 146, 152, 158, 165, 171, 177, 184, 191, 197, 204, 210, 216, 223, 229, 235, 241, 246, 252, 257, 262, 266, 271, 274, 278, 281, 284, 286, 288, 289, 290, 291, 290, 289, 287, 284, 281, 277, 273, 268, 262, 256, 250, 243, 236, 229, 222, 214, 206, 198, 191, 183, 175, 167, 159, 152, 145, 138, 131, 125, 119, 113, 108, 104, 100, 97, 94, 92, 91, 91]; + let eyeFrameIndex = 0; + let lastTimestamp = 0; + const eyeFrameDuration = 33; // 眼睛动画每帧 33ms + let currentGhostY = 567; // 当前 ghost Y 位置 + let currentSilkieY = 91; // 当前 silkie Y 位置 + let currentGhostA = 255; // 当前 ghost alpha 值 + + function lerp(start, end, t) { + return start + (end - start) * t; + } + + function draw(timestamp) { + const elapsed = lastTimestamp ? timestamp - lastTimestamp : 0; + + // 更新眼睛的动画 + if (elapsed >= eyeFrameDuration) { + const framesToUpdate = Math.floor(elapsed / eyeFrameDuration); + eyeFrameIndex = (eyeFrameIndex + framesToUpdate) % eyesFrames.length; + lastTimestamp += framesToUpdate * eyeFrameDuration; // 更新 lastTimestamp 以跳过到新帧 + } else if (!lastTimestamp) { + lastTimestamp = timestamp; // 第一次绘制时设置 lastTimestamp + } + + // 清除上一次绘制 + ctx.clearRect(0, 0, canvas.width, canvas.height); + // 重新绘制固定图像 + ctx.drawImage(img, 0, 0, cdkoWidth, cdkoHeight, 0, 0, cdkoWidth, cdkoHeight); // cdko 图 + ctx.drawImage(img, 1553, eyesFrames[eyeFrameIndex] * eyeHeight, eyeWidth, eyeHeight, 740, 498, eyeWidth, eyeHeight); + + // 计算 ghost 和 silkie 的帧索引 + const petFrameIndex = Math.floor(timestamp / 33) % ghostFrames.length; + const nextPetFrameIndex = (petFrameIndex + 1) % ghostFrames.length; + + const thisSilkieY = currentSilkieY; // 使用当前 silkie Y 位置 + const nextSilkieY = silkieFrames[nextPetFrameIndex]; + const thisGhostY = currentGhostY; // 使用当前 ghost Y 位置 + const nextGhostY = ghostFrames[nextPetFrameIndex][0]; + const thisGhostA = currentGhostA; // 使用当前 ghost alpha 值 + const nextGhostA = ghostFrames[nextPetFrameIndex][1]; + + // 计算插值比例 + const interpR = (elapsed % 33) / 33; + currentGhostY = lerp(thisGhostY, nextGhostY, interpR); // 更新当前 ghost Y 位置 + currentGhostA = lerp(thisGhostA, nextGhostA, interpR); // 更新当前 ghost alpha 值 + currentSilkieY = lerp(thisSilkieY, nextSilkieY, interpR); // 更新当前 silkie Y 位置 + + ctx.globalAlpha = currentGhostA / 255; + ctx.drawImage(img, 1377, 839, ghostWidth, ghostHeight, 1085, currentGhostY, ghostWidth, ghostHeight); // ghost 图 + ctx.globalAlpha = 1.0; + ctx.drawImage(img, 1377, 0, silkieWidth, silkieHeight, 424, currentSilkieY, silkieWidth, silkieHeight); // silkie 图 + window.requestAnimationFrame(draw); + } + + window.requestAnimationFrame(draw); + }; + + } + + render() { + return ( + + ); + } +} + +export default CDkoHalloweenDrawerCanvasComponent; \ No newline at end of file diff --git a/app/javascript/styles/ffxiv/drawer_cdko.scss b/app/javascript/styles/ffxiv/drawer_cdko.scss deleted file mode 100644 index a3395244df529a..00000000000000 --- a/app/javascript/styles/ffxiv/drawer_cdko.scss +++ /dev/null @@ -1,15 +0,0 @@ -.drawer__inner__mastodon { - background: #515151 !important; - >img { - display: none; - object-position: bottom center !important; - width: 100% !important; - content: url(https://media.monado.ren/site_uploads/241028/d77d4f9c36b51663f8d0d66fd84a63f2.png) !important; - } - - >video { - display: block; - object-position: bottom center !important; - width: 100% !important; - } -} \ No newline at end of file diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index d1c2db8a089226..3c0d692e808891 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2691,22 +2691,11 @@ a.account__display-name { flex: 1; min-height: 47px; display: none; - - > img { + > canvas { display: block; object-fit: contain; - object-position: bottom left; - width: 85%; - height: 100%; - pointer-events: none; - user-drag: none; - user-select: none; - } - > video { - display: none; - object-fit: contain; - object-position: bottom left; - width: 85%; + object-position: bottom center; + width: 100%; height: 100%; pointer-events: none; user-drag: none;