From 8c3d5b2dc1bb625451e9cd0795e913e56acf6a10 Mon Sep 17 00:00:00 2001 From: "wkylin.w@gmail.com" Date: Thu, 26 Dec 2024 15:51:43 +0800 Subject: [PATCH] feat: motion react --- .../stateless/DynamicBackground/icons.js | 35 +++++ .../DynamicBackground/icons/angular.svg | 1 + .../DynamicBackground/icons/deno.svg | 1 + .../DynamicBackground/icons/javascript.svg | 1 + .../stateless/DynamicBackground/icons/npm.svg | 1 + .../DynamicBackground/icons/react.svg | 10 ++ .../DynamicBackground/icons/redux.svg | 1 + .../DynamicBackground/icons/typescript.svg | 1 + .../stateless/DynamicBackground/icons/vue.svg | 1 + .../stateless/DynamicBackground/index.jsx | 127 +++++++++++++++++ .../DynamicBackground/index.module.less | 108 +++++++++++++++ src/pages/home/index.jsx | 33 +++-- src/pages/motion/index.jsx | 130 ++++++++---------- 13 files changed, 366 insertions(+), 84 deletions(-) create mode 100644 src/components/stateless/DynamicBackground/icons.js create mode 100644 src/components/stateless/DynamicBackground/icons/angular.svg create mode 100644 src/components/stateless/DynamicBackground/icons/deno.svg create mode 100644 src/components/stateless/DynamicBackground/icons/javascript.svg create mode 100644 src/components/stateless/DynamicBackground/icons/npm.svg create mode 100644 src/components/stateless/DynamicBackground/icons/react.svg create mode 100644 src/components/stateless/DynamicBackground/icons/redux.svg create mode 100644 src/components/stateless/DynamicBackground/icons/typescript.svg create mode 100644 src/components/stateless/DynamicBackground/icons/vue.svg create mode 100644 src/components/stateless/DynamicBackground/index.jsx create mode 100644 src/components/stateless/DynamicBackground/index.module.less diff --git a/src/components/stateless/DynamicBackground/icons.js b/src/components/stateless/DynamicBackground/icons.js new file mode 100644 index 00000000..180d9282 --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons.js @@ -0,0 +1,35 @@ +import React from 'react' +import ReactIcon from './icons/react.svg' +import AngularIcon from './icons/angular.svg' +import JSIcon from './icons/javascript.svg' +import NPMIcon from './icons/npm.svg' +import TSIcon from './icons/typescript.svg' +import VueIcon from './icons/vue.svg' +import ReduxIcon from './icons/redux.svg' + +const icons = [ + { size: 'md', type: 'outline', icon: null }, + { size: 'lg', type: 'fill', icon: }, + { size: 'sm', type: 'outline', icon: null }, + { size: 'lg', type: 'outline', icon: }, + { size: 'md', type: 'outline', icon: null }, + { size: 'md', type: 'fill', icon: }, + { size: 'sm', type: 'outline', icon: null }, + { size: 'md', type: 'outline', icon: null }, + { size: 'sm', type: 'outline', icon: null }, + { size: 'lg', type: 'outline', icon: }, + { size: 'md', type: 'fill', icon: null }, + { size: 'md', type: 'outline', icon: null }, + { size: 'md', type: 'outline', icon: null }, + { size: 'sm', type: 'fill', icon: }, + { size: 'sm', type: 'fill', icon: null }, + { size: 'md', type: 'outline', icon: null }, + { size: 'lg', type: 'fill', icon: }, + { size: 'sm', type: 'outline', icon: null }, + { size: 'sm', type: 'outline', icon: null }, + { size: 'md', type: 'fill', icon: }, + { size: 'sm', type: 'outline', icon: null }, + { size: 'sm', type: 'outline', icon: null }, +] + +export default icons diff --git a/src/components/stateless/DynamicBackground/icons/angular.svg b/src/components/stateless/DynamicBackground/icons/angular.svg new file mode 100644 index 00000000..eaf72674 --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons/angular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/stateless/DynamicBackground/icons/deno.svg b/src/components/stateless/DynamicBackground/icons/deno.svg new file mode 100644 index 00000000..4ba620d5 --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons/deno.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/stateless/DynamicBackground/icons/javascript.svg b/src/components/stateless/DynamicBackground/icons/javascript.svg new file mode 100644 index 00000000..ff5b132a --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons/javascript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/stateless/DynamicBackground/icons/npm.svg b/src/components/stateless/DynamicBackground/icons/npm.svg new file mode 100644 index 00000000..b04d3892 --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons/npm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/stateless/DynamicBackground/icons/react.svg b/src/components/stateless/DynamicBackground/icons/react.svg new file mode 100644 index 00000000..5434c606 --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons/react.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/components/stateless/DynamicBackground/icons/redux.svg b/src/components/stateless/DynamicBackground/icons/redux.svg new file mode 100644 index 00000000..04eb989f --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons/redux.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/stateless/DynamicBackground/icons/typescript.svg b/src/components/stateless/DynamicBackground/icons/typescript.svg new file mode 100644 index 00000000..c2e4d58e --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/stateless/DynamicBackground/icons/vue.svg b/src/components/stateless/DynamicBackground/icons/vue.svg new file mode 100644 index 00000000..4c66f2a6 --- /dev/null +++ b/src/components/stateless/DynamicBackground/icons/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/stateless/DynamicBackground/index.jsx b/src/components/stateless/DynamicBackground/index.jsx new file mode 100644 index 00000000..689fc944 --- /dev/null +++ b/src/components/stateless/DynamicBackground/index.jsx @@ -0,0 +1,127 @@ +import React, { useEffect, useRef, useState } from 'react' +import { useDebouncedCallback } from 'use-debounce' +import icons from './icons' +import styles from './index.module.less' + +const setThresholds = (size, x, y) => { + if (size === 'sm') { + return { + hoverX: x * 0.03, + hoverY: y * 0.03, + } + } + + if (size === 'md') { + return { + hoverX: x * 0.06, + hoverY: y * 0.06, + } + } + + if (size === 'lg') { + return { + hoverX: x * 0.1, + hoverY: y * 0.1, + } + } +} + +const DynamicBackground = () => { + const [hasMounted, setHasMounted] = useState(false) + const heroRef = useRef() + const iconsRef = useRef([]) + const iconCount = icons?.length + const { offsetWidth: width, offsetHeight: height } = heroRef?.current ?? {} + + const onHover = ({ clientX, clientY }) => { + iconsRef.current.forEach((item) => { + const { xPos, yPos } = item?.initialPositions ?? {} + const size = item.size + const { hoverX, hoverY } = setThresholds(size, clientX, clientY) + + if (item.refItem) { + item.refItem.style.transform = `translate(${xPos + hoverX}px, ${yPos + hoverY}px)` + } + }) + } + const onHoverDebounced = useDebouncedCallback(onHover, 10) + + useEffect(() => { + setHasMounted(true) + const refVal = heroRef?.current + if (!refVal) return + refVal.addEventListener('mousemove', onHoverDebounced) + refVal.addEventListener('resize', onHoverDebounced) + + return () => { + if (!refVal) return + refVal.removeEventListener('mousemove', onHoverDebounced) + refVal.removeEventListener('resize', onHoverDebounced) + } + }, []) + + return ( +
+ {hasMounted && ( +
+ {icons.map(({ size, type, icon }, i) => { + const rows = Math.ceil(Math.sqrt(iconCount)) + const cols = Math.ceil(iconCount / rows) + + const cellWidth = width / cols + const cellHeight = height / rows + + const row = Math.floor(i / cols) + const col = i % cols + + const xOffset = Math.random() * (cellWidth - 75) + const yOffset = Math.random() * (cellHeight - 75) + + const baseX = col * cellWidth + const baseY = row * cellHeight + + const xPos = baseX + xOffset + const yPos = baseY + yOffset + + return ( + + (iconsRef.current[i] = { + refItem, + initialPositions: { + xPos, + yPos, + }, + size, + }) + } + className={`${styles.icon} ${styles[type]} ${styles[size]}`} + style={{ + transform: `translateX(${xPos}px) translateY(${yPos}px)`, + }} + > + {icon && icon} + + ) + })} +
+ )} +
+

+ Grow your
Frontend Skills +

+

+ with React, Angular, Vue, Typescript, Redux,{' '} + Node, GraphQL,CSS, HTML, Webpack,{' '} + Rollup, ESLint, Prettier, Stylelint,PostCSS,{' '} + Styled Components, Emotion, Material UI, Ant Design,{' '} + Bootstrap, Foundation,React Native, Flutter,{' '} + Python, Go,JavaScript, and NPM +

+
+
+ ) +} + +export default DynamicBackground diff --git a/src/components/stateless/DynamicBackground/index.module.less b/src/components/stateless/DynamicBackground/index.module.less new file mode 100644 index 00000000..85fdd061 --- /dev/null +++ b/src/components/stateless/DynamicBackground/index.module.less @@ -0,0 +1,108 @@ +.hero { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + margin: 0 auto; + padding: 50px 0; + position: relative; + height: 600px; + overflow: hidden; + background: radial-gradient(circle, hsl(30deg 100% 8% / 100%) 0%, rgb(0 0 0) 100%); +} + +.heroText { + text-align: center; + color: #fff; + max-width: 750px; + z-index: 10; + + h1 { + font-size: 76px; + margin: 0 0 30px; + font-weight: 300; + + span { + background: linear-gradient(30deg, #ff8000 40%, #ad1d1d 70%); + background-clip: text; + color: transparent; + } + } + + h3 { + font-size: 20px; + font-weight: 300; + } + + a { + margin-top: 20px; + display: inline-block; + color: #fff; + text-decoration: none; + font-size: 20px; + border-radius: 8px; + background: #ad1d1d; + padding: 20px 30px; + } +} + +.iconLayer { + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 100%; +} + +.icon { + position: absolute; + border-radius: 12px; + transition: transform 1s ease-out; + animation: icon-fade-in 7s ease forwards; + padding: 10px; + + svg { + width: 100%; + height: 100%; + } + + path { + color: #fff; + } + + &.outline { + border: 1px solid #414141; + } + + &.fill { + background: #414141; + } + + &.sm { + height: 40px; + width: 40px; + z-index: 1; + } + + &.md { + height: 60px; + width: 60px; + z-index: 3; + } + + &.lg { + height: 80px; + width: 80px; + z-index: 5; + } +} + +@keyframes icon-fade-in { + from { + opacity: 0; + } + + to { + opacity: 0.6; + } +} diff --git a/src/pages/home/index.jsx b/src/pages/home/index.jsx index 4f4624c6..56a64c19 100644 --- a/src/pages/home/index.jsx +++ b/src/pages/home/index.jsx @@ -33,7 +33,7 @@ import TagCloud from '@stateless/TagCloud' import ShiCode from '@stateless/ShiCode' // import SlideLinear from '@stateless/SlideLinear' // import Masonry from '@container/masonryContainer' - +import DynamicBackground from '@stateless/DynamicBackground' import { oneApiChat, prettyObject } from '@utils/aidFn' import { fireConfetti } from '@utils/confetti' @@ -382,18 +382,20 @@ const Home = () => {
- - - - - - - - - - - - +
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -434,7 +436,7 @@ const Home = () => { -
+
= ({ item }) => { @@ -449,6 +451,9 @@ const GroceryItem: React.FC = ({ item }) => { `} />
+
+ +
) } diff --git a/src/pages/motion/index.jsx b/src/pages/motion/index.jsx index e0dd0120..2c6dbd9a 100644 --- a/src/pages/motion/index.jsx +++ b/src/pages/motion/index.jsx @@ -265,15 +265,9 @@ const ParallaxVert = () => { > -
{ repeat: Infinity, }} > -
+ */} +
+ { + console.log(latest.x, latest.y) + }} + onAnimationStart={() => { + console.log('Animation started') + setIsAnimation(true) + }} + onAnimationComplete={(definition) => { + setIsAnimation(false) + console.log('Completed animating', definition) + }} + > +
+ { + await controller2.start({ x: 100 }) + await controller2.start({ y: -200 }) + await controller2.start({ x: -100 }) + controller2.start({ y: 0 }) + }} + > + 开始 + +
+
{ width: 800, height: 400, border: '1px solid #aaa', - margin: '0 auto', + margin: '20px auto', }} ref={constraintsRef} > @@ -320,7 +368,7 @@ const ParallaxVert = () => { position: 'sticky', top: '40px', left: 0, - zIndex: 10, + zIndex: 3, }} > 拖动元素 @@ -389,65 +437,7 @@ const ParallaxVert = () => { -
- { - console.log(latest.x, latest.y) - }} - onAnimationStart={() => { - console.log('Animation started') - setIsAnimation(true) - }} - onAnimationComplete={(definition) => { - setIsAnimation(false) - console.log('Completed animating', definition) - }} - > -
- { - await controller2.start({ x: 100 }) - await controller2.start({ y: -200 }) - await controller2.start({ x: -100 }) - controller2.start({ y: 0 }) - }} - > - 开始 - -
-
+