react+gsap+Draggable实现拖动撕开贴纸显示背部文字效果代码
代码语言:html
所属分类:拖放
代码描述:react+gsap+Draggable实现拖动撕开贴纸显示背部文字效果代码
代码标签: react gsap Draggable 拖动 撕开 贴纸 显示 背部 文字 代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <link type="text/css" rel="stylesheet" href="//repo.bfw.wiki/bfwrepo/css/normalize.5.0.css"> <style> *, *:after, *:before { box-sizing: border-box; } :root { --bg: hsl(0 0% 98%); } body { display: grid; place-items: center; min-height: 100vh; font-family: "SF Pro Text", "SF Pro Icons", "AOS Icons", "Helvetica Neue", Helvetica, Arial, sans-serif, system-ui; overflow: hidden; background: var(--bg); } section { height: 100vh; display: grid; place-items: center; position: fixed; inset: 0; } .tear-strip:focus-within { outline: 10px solid hsl(0 0% 0% / 0.15); } .strip-adder { position: fixed; top: 2rem; right: 2rem; width: 48px; aspect-ratio: 1; z-index: 22; border-radius: 50%; border: 0; display: grid; place-items: center; padding: 0; background: hsl(0 0% 90% / var(--alpha, 0)); transition: background 0.2s; } .strip-adder svg { width: 65%; color: hsl(0 0% 10% / var(--alpha, 0.5)); transition: color 0.2s; } .strip-adder:is(:hover, :focus-visible) { --alpha: 1; } .reference { opacity: 1; position: fixed; top: 50%; left: 50%; translate: -50% -150%; } .tear-strip { font-size: 1.3rem; font-weight: bold; width: clamp(300px, 470px, 28vw); width: 340px; height: 78px; display: grid; place-items: center; /* translate: 0 -80%;*/ position: relative; border: 4px dashed hsl(0 0% 91%); border-radius: 100px; background: linear-gradient(hsl(0 0% 91%), hsl(0 0% 91%)) padding-box; color: hsl(0, 0%, 70%); } .tear-strip__content { position: absolute; font-size: 1rem; text-align: right; } .tear-strip__strip { position: absolute; inset: 0; background: var(--bg); display: flex; border-radius: 100px; align-items: center; justify-content: center; -webkit-clip-path: inset(-100% 0 -100% 1px); clip-path: inset(-100% 0 -100% 1px); color: hsl(0, 0%, 71%); font-weight: 500; } .tear-strip__shadow { position: absolute; height: 100%; width: 20px; background: linear-gradient(90deg, transparent, hsl(0 0% 10% / 0.5)); filter: blur(8px); transform-origin: 100% 50%; left: 0; opacity: 0; } .tear-strip__back { position: absolute; height: 100%; width: 100%; border-radius: 100px; right: 100%; } .tear-strip__backing { background: linear-gradient(90deg, hsl(0 0% calc(var(--tab-darkness, 40) * 1%) / var(--bg-alpha, 1)), hsl(0 0% 100% / var(--bg-alpha, 1)), hsl(0 0% 80% / var(--bg-alpha, 1))); background-position: 100% 50%; background-repeat: no-repeat; background-color: hsl(0 0% 93%); background-size: calc(var(--bg-size, 0) * 1px) 100%; position: absolute; inset: 0; border-radius: 1000px; } .tear-strip__backing::before { content: ""; position: absolute; inset: 0 -8px 0 0; filter: blur(4px); background: radial-gradient(hsl(0 0% 10% / 0.5), transparent 80%); border-radius: 1000px; z-index: -1; opacity: var(--shadow-reveal, 0); } .tear-strip__back-shadow { position: absolute; border-radius: 1000px; background: transparent; right: 0; top: 50%; height: 100%; translate: 0 -50%; width: calc((var(--shadow-width) * var(--shadow-multiplier, 0.8)) * 1px); z-index: -1; min-width: 100px; box-shadow: 0 0 calc(var(--shadow-spread, 0) * 60px) hsl(10 0% 50% / 0.35); } .tear-strip__strip svg { background: hsl(78, 75%, 57%); border-radius: 50%; width: 48px; padding: 10px; position: absolute; left: 11px; color: white; stroke-width: 2.5px; transform-origin: 100% 50%; } .tear-strip__handle { position: absolute; left: 0; top: 0; bottom: 0; aspect-ratio: 1; translate: 0% 0; background: hsl(280 0% 75% / var(--alpha, 0)); border-radius: 50%; scale: 1.35; transition: background 0.2s; } .tear-strip__handle:hover:not(:active) { --alpha: 0.25; } .tear-strip__handle::before { content: ""; width: 200%; position: absolute; height: 100%; left: 50%; translate: -50% 0; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } </style> </head> <body > <div id="app"></div> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/babel.7.18.13.js"></script> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/gsap.3.9.1.js"></script> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/Draggable3.min.js"></script> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/react.production.17.1.js"></script> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/react-dom.production.17.1.js"></script> <script type="text/babel"> const { react } = window.React; const { render } = window.ReactDOM; gsap.registerPlugin(Draggable) const ROOT_NODE = document.querySelector('#app') const TearStrip = ({ children }) => { const stripRef = React.useRef(null) const tabRef = React.useRef(null) const backingRef = React.useRef(null) const handleRef = React.useRef(null) const iconRef = React.useRef(null) const draggableRef = React.useRef(null) const shadowRef = React.useRef(null) const proxyRef = React.useRef(document.createElement('div')) const [torn, setTorn] = React.useState(false) const audioRef = React.useRef(new Audio('/tear_open.mp3')) // Handle all the draggable stuff, etc. React.useEffect(() => { // Need to load the audio so I can get the duration const DISTANCE_OF_RESISTANCE = stripRef.current.offsetWidth * 0.25 gsap.set(handleRef.current, { x: 0, y: 0, }) // Create the Draggable instance which is where all the interactivity happens draggableRef.current = Draggable.create(proxyRef.current, { // NOTE:: Instances of "this" refer to the Draggable if you're new to Draggable type: 'x,y', trigger: handleRef.current, allowContextMenu: true, dragResistance: 0.99, // onDragStart: () => { // audioRef.current.playbackRate = 0.1 // audioRef.current.play() // }, onDrag: function (event) { // If you void the dragging by going up/down too much before tearing if (this.__void) return false // If the strip is torn, then you can move it around the page if (this.__torn) { return gsap.to(tabRef.current, { x: this.x - stripRef.current.offsetWidth * 2, y: this.y, duration: 0.1, }) } // Void the drag if you go too high up or down const HANDLE_HEIGHT = handleRef.current.offsetHeight if ( this.y < this.startY - HANDLE_HEIGHT * 0.5 || this.y > this.startY + HANDLE_HEIGHT * 0.5 ) { return (this.__void = true) } // Update the dragResistance as you tear. // TODO:: Would be ideal to have some resistance added again before the end. // But, it flakes out. Could be a GSAP bug :thinking: if (this.x > this.startX) { // Trick here is to clamp agains the current dragResistance. It can only go down. this.dragResistance = gsap.utils.clamp( 0, this.dragResistance, gsap.utils.mapRange(0, DISTANCE_OF_RESISTANCE, 0.99, 0, this.x) ) } if (!this.__torn) { // Map the clip of the strip based on the handle position const clip = gsap.utils.mapRange( 0, stripRef.current.offsetWidth * 2, 0, stripRef.current.offsetWidth, this.x ) gsap.set(tabRef.current, { clipPath: `inset(-100% -1000% -100% ${clip}px)`, }) } // Set custom props for use across the strip gsap.set(stripRef.current, { '--tab-darkness': gsap.utils.clamp( 10, 80, gsap.utils.mapRange(0, stripRef.current.offsetWidth, 10, 80, this.x) ), '--shadow-spread': gsap.utils.clamp( 0, 1, gsap.utils.mapRange( stripRef.current.offsetWidth * 0.25, stripRef.current.offsetWidth, 0, 1, this.x ) ), '--shadow-reveal': gsap.utils.clamp(0, 1, gsap.utils.mapRange(stripRef.current.offsetWidth * 0.1, stripRef.current.offsetWidth * 0.2, 0, 1, this.x)), '--shadow-width': this.x * 0.5, '--bg-size': this.x * 0.5, '--shadow-multiplier': gsap.utils.clamp( 0.8, 0.9, gsap.utils.mapRange( stripRef.current.offsetWidth, stripRef.current.offsetWidth * 2, 0.8, 0.9, this.x ) ), }) // Move the icon gsap.set(iconRef.current, { .........完整代码请登录后点击上方下载按钮下载查看
网友评论0