react+use-web-animations实现横向和竖向图标菜单滑动效果代码
代码语言:html
所属分类:菜单导航
代码描述:react+use-web-animations实现横向和竖向图标菜单滑动效果代码
代码标签: react use-web-animations 横向 竖向 图标 菜单 滑动
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <link rel='stylesheet' href='https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz@0,9..40;1,9..40&display=swap'> <style> * { border: 0; box-sizing: border-box; margin: 0; padding: 0; } :root { --bg: hsl(0,0%,90%); --fg: hsl(0,0%,10%); --trans-dur: 0.3s; font-size: calc(16px + (32 - 16) * (100vw - 240px) / (1920 - 240)); } body, button { color: var(--fg); font: 1em/1.5 "DM Sans", sans-serif; transition: background-color var(--trans-dur), color var(--trans-dur); } body { background-color: var(--bg); transition: background-color var(--trans-dur); } button { outline: transparent; -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent; } main { display: flex; flex-direction: column; gap: 3em; align-items: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } main > svg { position: fixed; } .toolbar { background-color: var(--fg); border-radius: 1em; display: inline-flex; gap: 0.125em; align-items: center; position: relative; padding: 0.125em; min-width: 2em; height: 2em; transition: background-color var(--trans-dur); } .toolbar__button { background-color: rgba(128, 128, 128, 0); border-radius: 50%; cursor: pointer; display: flex; position: relative; width: 1.75em; height: 1.75em; } .toolbar__button[aria-pressed=false]:hover { background-color: rgba(128, 128, 128, 0.5); } .toolbar__button-tip { background-color: var(--fg); border-radius: 0.5625rem; color: var(--bg); font-size: 0.625em; line-height: 1; margin-top: 0.375rem; opacity: 0; padding: 0.25rem 0.5rem; pointer-events: none; position: absolute; top: 100%; left: 50%; transform: translate(-50%, -25%); transition: background-color var(--trans-dur), color var(--trans-dur), opacity var(--trans-dur), transform var(--trans-dur), visibility var(--trans-dur); visibility: hidden; } .toolbar__button:focus-visible .toolbar__button-tip, .toolbar__button:hover .toolbar__button-tip { opacity: 1; visibility: visible; transform: translate(-50%, 0); } .toolbar__highlight { background-color: white; border-radius: 0.875em; margin: 0.125em; mix-blend-mode: difference; pointer-events: none; position: absolute; top: 0; left: 0; width: 1.75em; height: 1.75em; transition: background-color var(--trans-dur); } [dir=rtl] .toolbar__highlight { right: 0; left: auto; } .toolbar__icon { color: var(--bg); display: block; margin: auto; width: 1em; height: auto; transition: color var(--trans-dur); } .toolbar--vertical { flex-direction: column; width: 2em; min-height: 2em; height: auto; } .toolbar--vertical .toolbar__button-tip { margin-inline-start: 0.375rem; margin-top: 0; top: 50%; left: 100%; transform: translate(-25%, -50%); } [dir=rtl] .toolbar--vertical .toolbar__button-tip { right: 100%; left: auto; transform: translate(25%, -50%); } .toolbar--vertical .toolbar__button:focus-visible .toolbar__button-tip, .toolbar--vertical .toolbar__button:hover .toolbar__button-tip { transform: translate(0, -50%); } /* Dark theme */ @media (prefers-color-scheme: dark) { :root { --bg: hsl(0,0%,10%); --fg: hsl(0,0%,90%); } } /* Beyond mobile */ @media (min-width: 768px) { main { flex-direction: row; } } </style> </head> <body translate="no"> <div id="root"></div> <script type="module"> import React, { createContext, StrictMode, useContext, useRef, useState } from "https://esm.sh/react"; import { createRoot } from "https://esm.sh/react-dom/client"; import useWebAnimations from "https://esm.sh/@wellyshen/use-web-animations"; const ToolbarContext = createContext(undefined); const ToolbarProvider = ({ children }) => { const [tool, setTool] = useState("select"); return (React.createElement(ToolbarContext.Provider, { value: { tool, setTool } }, children)); }; const useTool = () => { const context = useContext(ToolbarContext); if (!context) { throw new Error("useTool must be used inside ToolbarProvider"); } return context; }; createRoot(document.getElementById("root")).render(React.createElement(StrictMode, null, React.createElement("main", null, React.createElement(ToolbarIconSprites, null), React.createElement(ToolbarProvider, null, React.createElement(Toolbar, { name: "Horizontal" })), React.createElement(ToolbarProvider, null, React.createElement(Toolbar, { name: "Vertical", orientation: "vertical" }))))); function Toolbar({ name, orientation = "horizontal" }) { const { tool, setTool } = useTool(); const [toolPrev, setToolPrev] = useState(tool); const toolbarRef = useRef(null); const { ref, animate } = useWebAnimations(); const isRTL = document.dir === "rtl"; const isVertical = orientation === "vertical"; const tools = [ { icon: "select", label: "Select", tool: "select" }, { icon: "draw", label: "Draw", tool: "draw" }, { icon: "fill", label: "Fill", tool: "fill" }, { icon: "erase", label: "Erase", tool: "erase" } ]; function handleKeyDown(e) { var _a; const toolButtons = (_a = toolbarRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll("[tabindex]"); if (!toolButtons) return; const toolIndex = tools.findIndex(item => item.tool === tool); const forward = () => { e.preventDefault(); const nextToolIndex = (toolIndex + 1) % tools.length; const toolName = tools[nextToolIndex].tool; setTool(() => { reAnimate(toolName); return toolName; }); toolButtons[nextToolIndex].focus(); }; const backward = () => { e.preventDefault(); let prevToolIndex = toolIndex - 1; if (prevToolIndex < 0) prevToolIndex = tools.length - 1; const toolName = tools[prevToolIndex].tool; setTool(() => { reAnimate(toolName); return toolName; }); toolButtons[prevToolIndex].focus(); }; switch (e.code) { case "ArrowRight": { if (isRTL) { backward(); } else { forward(); } break; } case "ArrowDown": { forward(); break; } case "ArrowLeft": { if (isRTL) { forward(); } else { backward(); .........完整代码请登录后点击上方下载按钮下载查看
网友评论0