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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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文…