dagger.js 實現「CSS 機械鍵盤」示例解讀(對比 React 版本)

0) 效果演示 (代碼地址)

CSS Mechanical Keyboard


1) 示例與來源

  • dagger.js 版本:本筆圍繞 CodePen 上的《CSS Mechanical Keyboard》的 dagger.js 改寫版進行解讀,核心思路是用 dagger 指令把純 CSS 藝術包裝成可復用的組件,并加入鍵盤事件與音效。
  • 原始作品:原作由 Yoav Kadosh 創作,是一個 純 CSS 的機械鍵盤(不依賴外部 JS),偏重 3D 視覺與陰影層疊技巧。
  • 本文對照:為便于理解,我們提供一個等價的 React 參考實現(并非作者官方版本),用于對比心智模型、代碼結構與工程復雜度。

👉 說明:原作側重 CSS 藝術;Dagger 版本在此基礎上,借助指令系統與模板,增強了可組合性和交互(按鍵高亮、鍵音)。


2) dagger.js 代碼結構速覽

下面片段來自示例的核心結構,已做適度壓縮與注釋,便于閱讀。

2.1 模塊與模板

<!-- 聲明模塊與模板的映射(同一 Pen 也可改為外鏈腳本模塊) -->
<script type="dagger/modules">
{"_": "#script","key": "#template_key","row": "#template_row","column": "#template_column"
}
</script><!-- 業務腳本(作為 dagger 模塊暴露函數) -->
<script type="dagger/script" id="script">export const load = () => ({set: new Set(),audio: new Audio("https://assets.codepen.io/5782383/keytype.mp3")});export const keyInit = (set, char, span = false) => ({char, span, active: set.has(char)});export const onKeyDown = ($event, set, audio) => {set.add($event.key);audio.pause(); audio.currentTime = 0; audio.play();};
</script><!-- 組件模板:Key / Row / Column -->
<template id="template_key"><div class="key" *class="{ span: $scope.span, active: $scope.set.has(char) }"><div class="side"></div><div class="top"></div><div class="char">${ char }</div></div>
</template><template id="template_row"><div class="row"><template @slot></template></div>
</template><template id="template_column"><div class="column"><template @slot></template></div>
</template>

2.2 頁面與交互

<div class="keyboard"dg-cloak+load+keydown#target:document="onKeyDown($event, set, audio)"+keyup#target:document="set.delete($event.key)"><column><row><key *each="['7','8','9']" +load="keyInit(set, item)"></key></row><row><key *each="['4','5','6']" +load="keyInit(set, item)"></key></row><row><key *each="['1','2','3']" +load="keyInit(set, item)"></key></row><row><key +load="keyInit(set, '0', true)"></key><key +load="keyInit(set, '.')"></key></row></column><column><key +load="keyInit(set, '+', true)"></key><key +load="keyInit(set, '-', true)"></key></column><div class="shade"></div><div class="cover"></div>
</div>

要點解讀

  • +load:組件/元素裝載時初始化作用域,返回 { set, audio } 等狀態對象。
  • *each:把字符數組映射為一組 <key> 子組件。
  • *class:根據 set 中是否包含字符切換 active/span 類名。
  • +keydown/+keyup#target:document:把監聽目標直接綁定到 document,控制全局按鍵高亮與刪除狀態
  • 模板 <template @slot>Row/Column 像容器組件一樣承載子節點(對標 React 的 children)。

3) 交互與狀態

  • 按鍵狀態:用 Set 存當前被按下的字符,keydownaddkeyupdelete
  • 音效Audio 對象復用;每次按鍵前 pause 并重置 currentTime,避免疊音。
  • 高亮*class$scope.set.has(char) 實時驅動。

4) 樣式與 3D 視覺要點(概覽)

  • 主題色/陰影:SCSS 變量(如 $color-gray-*$color-orange-*)集中管理。
  • 立體感:transform: rotateX(...) rotateZ(...)transform-style: preserve-3d + 多層 box-shadow
  • 自定義函數:@function layered_shadow(...) 構造層疊陰影,營造“厚重”的機械感。

視覺仍然由 純 CSS/SCSS 驅動;dagger.js 只負責結構/交互與狀態膠合。


5) React 參考實現(等價思路)

下例演示若用 React 實現同等交互,核心包括:組件拆分、全局鍵盤事件、Set 狀態與音效復用。代碼僅作對照示例

import React, { useEffect, useMemo, useRef, useState } from "react";function useKeyboardAudio(src) {const audioRef = useRef(null);useEffect(() => { audioRef.current = new Audio(src); }, [src]);const play = () => {const a = audioRef.current;if (!a) return;a.pause(); a.currentTime = 0; a.play();};return play;
}function Key({ char, active }) {return (<div className={`key ${active ? "active" : ""}`}><div className="side" /><div className="top" /><div className="char">{char}</div></div>);
}function Row({ children })   { return <div className="row">{children}</div>; }
function Column({ children }){ return <div className="column">{children}</div>; }export default function Keyboard() {const [down, setDown] = useState(() => new Set());const play = useKeyboardAudio("https://assets.codepen.io/5782383/keytype.mp3");useEffect(() => {const onKeyDown = (e) => {// 采用不可變更新觸發重渲染setDown(prev => {if (prev.has(e.key)) return prev;const next = new Set(prev);next.add(e.key);play();return next;});};const onKeyUp = (e) => setDown(prev => {if (!prev.has(e.key)) return prev;const next = new Set(prev);next.delete(e.key);return next;});document.addEventListener("keydown", onKeyDown);document.addEventListener("keyup", onKeyUp);return () => {document.removeEventListener("keydown", onKeyDown);document.removeEventListener("keyup", onKeyUp);};}, [play]);const rows = useMemo(() => [["7","8","9"],["4","5","6"],["1","2","3"],], []);return (<div className="keyboard"><Column>{rows.map((arr, i) => (<Row key={i}>{arr.map(c => <Key key={c} char={c} active={down.has(c)} />)}</Row>))}<Row><Key char="0" active={down.has("0")} /><Key char="." active={down.has(".")} /></Row></Column><Column><Key char="+" active={down.has("+")} /><Key char="-" active={down.has("-")} /></Column><div className="shade" /><div className="cover" /></div>);
}

樣式(SCSS)基本可直接復用原作;必要時把 *class 的條件改為 React 的類名拼接邏輯。


6) dagger.js vs React:對照表

維度dagger.js 實現React 等價實現
心智模型聲明式指令*each*class+load、事件 #target:document)+ 模板插槽組件 + JSX,狀態驅動渲染,DOM 由虛擬 DOM 協調
狀態管理直接在作用域返回 { set, audio }Set 原地增刪useState / useRef;常以不可變更新觸發重渲染
事件綁定+keydown/+keyup#target:document 語法內置useEffect 手動綁定/卸載 document 事件
模板/組合<template @slot> 容器模式;無需打包即可模塊化children 組合;通常依賴打包或 Babel/JSX
運行與構建零構建可運行(原生 ESM / Script Type 支持)常規項目多用打包鏈路(Vite/webpack);CodePen 可臨時用 Babel
代碼體量交互 JS 極少,主要重用 CSS 視覺交互樣板(hooks/不可變更新)略多
適用場景低門檻改造 CSS 藝術為可復用組件/小交互生態完備、可擴展體系更強,適合復雜應用

7) 什么時候選 dagger.js?

  • 你已有一份 純 CSS 藝術/動效,想快速加上鍵盤/鼠標交互與組件化復用
  • 希望 零構建 上線(靜態托管 / Edge 環境)并保持極低的引入成本;
  • 更傾向原生 DOM 與語義化指令,不想維護冗長的狀態樣板。

8) 小結

  • 原作突出 CSS 3D 質感與陰影層疊;dagger.js 改寫把它“組件化 + 可交互化”。
  • 若用 React,實現同等功能也很直接,但需要一些 hooks 樣板與狀態不可變更新的心智模型。

本文內容就到這里,后續文章將為大家帶來更多案例和講解。

如果對dagger.js感興趣的話,請您點贊收藏、分享本系列文章,也歡迎留言或者私信作者提出問題和建議,您的關注是對我最大的支持和鼓勵。感謝您的閱讀,祝工作學習順利!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920453.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920453.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920453.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何檢查 Linux 系統的內存使用情況

內存管理是保持 Linux 系統平穩運行的重要組成部分。無論您是系統管理員、開發者&#xff0c;學會檢查 Linux 內存使用情況是確保系統穩定性和性能的關鍵步驟。本文將介紹在 Linux 環境下監控和檢查內存使用的幾種方法&#xff0c;包括命令行工具&#xff08;如 top、vmstat、p…

我店生活平臺是不是 “圈錢平臺”?揭開消費補貼新模式的面紗

近年來&#xff0c;本地生活服務領域涌現出諸多創新模式&#xff0c;其中“WO店”生活平臺憑借其獨特的全民補貼機制引發行業關注。在“圈錢平臺”質疑聲此起彼伏的背景下&#xff0c;這一模式究竟是商業創新還是資本游戲&#xff1f;本文將從商業模式、風險控制、用戶權益保障…

(LeetCode 每日一題) 1493. 刪掉一個元素以后全為 1 的最長子數組 (雙指針)

題目&#xff1a;1493. 刪掉一個元素以后全為 1 的最長子數組 思路&#xff1a;雙指針&#xff0c;時間復雜度0(n)。 C版本&#xff1a; class Solution { public:int longestSubarray(vector<int>& nums) {int ans0;int left0,cnt0;for(int i0;i<nums.size();i…

java去圖片水印的方法

下面我將從簡單到復雜&#xff0c;介紹幾種常見的 Java 去水印方法、適用的場景以及需要注意的事項。核心思路去水印的本質是&#xff1a;?用合理的背景內容替換水印區域的像素。方法一&#xff1a;覆蓋或裁剪&#xff08;適用于簡單情況&#xff09;這種方法不算是真正的“去…

刷題日記0828

今天開啟新篇章。面試經典150題。今日計劃5道。3/588. 合并兩個有序數組怎么樣不用sort把 nums2里的放進 nums1呢&#xff1f;看題解。看了&#xff0c;還是新開了個數組。做的還是挺快的&#xff0c;記得有一次面試就是這個題&#xff0c;沒想到居然是第一題 hhh。時間復雜度可…

網站開發用什么語言好

HTML、CSS 和 JavaScriptHTML 就像是網站的骨架&#xff0c;負責搭建網頁的結構&#xff1b;CSS 則是給網站穿上漂亮的衣服&#xff0c;讓它看起來賞心悅目&#xff1b;而 JavaScript 就如同賦予網站生命的靈魂&#xff0c;讓網頁能夠與用戶進行交互。據統計&#xff0c;全球超…

開源夜鶯里如何引用標簽和注解變量

今天遇到開源社區咨詢&#xff1a;夜鶯里如何引用標簽和注解變量&#xff1f;這個問題如果通讀文檔&#xff0c;其實也能找到答案&#xff0c;不過相關知識是散落在各處的&#xff0c;這里就集中說一下&#xff0c;方便大家查閱。 哪里可以引用標簽和注解變量 主要有兩個地方…

大數據的五大特征(5V模型)深度解讀

一、Volume&#xff08;體積&#xff09;&#xff1a;數據的“海洋” 定義&#xff1a;指數據的巨大體量。大數據的計量單位已經從傳統的GB、TB級躍升至PB、EB甚至ZB級。 深度解讀&#xff1a; “Volume”是大數據最顯而易見的特征。我們正生活在一個數據爆炸的時代&#xff1a…

基于SpringBoot的寵物領養服務系統【2026最新】

作者&#xff1a;計算機學姐 開發技術&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源碼”。 專欄推薦&#xff1a;前后端分離項目源碼、SpringBoot項目源碼、Vue項目源碼、SSM項目源碼、微信小程序源碼 精品專欄&#xff1a;…

AI 解決生活小事 2——用 AI 做一回新聞播客

哈嘍&#xff0c;各位C站的朋友們&#xff0c;我是極客團長&#xff0c;一位專注用AI解決生活小事的科技玩家&#xff0c;上一期我們聊了聊怎么用AI給電腦做“深度體檢”&#xff0c;把電腦里積攢多年的“垃圾”清理了個干凈。 那篇反響還不錯&#xff0c;看來大家跟我一樣&am…

Vue3 圖片加載失敗回退為默認圖:最簡、健壯的兩種實現(含完整代碼)

先上結論&#xff1a;給 <img> 綁定 error&#xff0c;在回調里將 src 切到默認頭像&#xff0c;并斷開二次觸發&#xff0c;配合 new URL(..., import.meta.url).href 解析靜態資源路徑&#xff0c;可靠、可維護。 場景與目標 登錄用戶有頭像 URL&#xff0c;但可能 40…

VisionPro聯合編程控件導入WinFrom以及VS卡死問題

在工業自動化領域&#xff0c;C#和VisionPro都是備受矚目的工具。C#是一種功能強大的編程語言&#xff0c;廣泛應用于Windows平臺上的應用程序開發。而VisionPro則是一款視覺檢測軟件&#xff0c;廣泛應用于自動化生產線上的產品質量檢測。將C#與VisionPro結合使用&#xff0c;…

練習spring mvc

1. 項目結構總結 這個Spring MVC項目采用Maven管理&#xff0c;遵循標準的Web項目結構。以下是詳細的文件級別結構&#xff1a; 核心目錄結構 springmvc_helloword/ ├── .idea/ # IDEA項目配置目錄 │ ├── artifacts/ # 項目打包配置…

postgreSql遠程連接數據庫總是超時斷開?

問題&#xff1a;postgresql經常遇到連接中斷的情況&#xff0c;程序幾分鐘就會斷一次很難受。 pg的日志大量報錯&#xff1a; 2025-08-27 11:05:43.967 CST [26462] LOG: could not receive data from client: Connection reset by peer 2025-08-27 11:05:43.967 CST [2625…

【Java基礎】Java數據結構深度解析:Array、ArrayList與LinkedList的對比與實踐

Java數據結構深度解析&#xff1a;Array、ArrayList與LinkedList的對比與實踐 在Java編程中&#xff0c;數據存儲與操作是最基礎的能力要求。Array&#xff08;數組&#xff09;、ArrayList&#xff08;動態數組&#xff09;與LinkedList&#xff08;雙向鏈表&#xff09;作為最…

Flask測試平臺開發,登陸重構

概述我們在開篇的時候實現了簡單的登陸功能&#xff0c;也實現了一個前后端聯調的登陸功能&#xff0c;但是你有沒有發現&#xff0c;那個登陸只是一個簡單的登陸&#xff0c;且密碼在接口返回的過程中是銘文密碼&#xff0c;在生產環境中使用肯定是不行的&#xff0c;一般密碼…

tiny4412 Qt環境搭建

1.硬件環境PC端&#xff1a;ubuntu18.04 開發板硬件平臺&#xff1a;tiny4412 內核版本&#xff1a;linux3.5 交叉編譯器&#xff1a;arm-linux-gcc Qt版本&#xff1a;Qt5.62.搭建ubuntu下Qt編譯環境1.在用戶目錄下的src_pack目錄下解壓。 [wbyqwbyq src_pack]$ pwd /home/wby…

將本地jar包推到遠程倉庫

前提條件&#xff0c;手里有個jar包想推到maven遠程倉庫 1. 在maven項目中&#xff0c;輸入腳本執行 2. 在電腦中打開PowerShell以管理員身份運行&#xff0c;輸入腳本執行 # 使用 Maven 將本地 JAR 文件上傳到遠程 Maven 倉庫&#xff08;PowerShell 版本&#xff09; # 注…

企業級監控可視化系統 Prometheus + Grafana

警報&#xff08;Alerting&#xff09;&#xff1a;使用 Prometheus 的 Alertmanager 或 Grafana 的內置告警功能&#xff0c;在指標異常時發送通知&#xff08;郵件、Slack、釘釘等&#xff09;。 服務發現&#xff1a;在云環境中&#xff08;Kubernetes, Consul等&#xff09…

極簡風格PDF格式轉換解決方案

雖然PDF非常適合于閱讀和分享&#xff0c;但有時我們需要對文檔做一些調整&#xff0c;如增加注釋、高亮重點信息或者填寫表單字段。 它的的界面設計簡潔&#xff0c;它有強大的格式轉換功能&#xff0c;不單單是將PDF轉換成word文檔或者PDF轉換 excel&#xff0c;還能將PDF文…