如何为DrawSVG动画按钮的OPEN文本添加下载触发功能?
解决下载按钮的触发问题
嘿,你的这个SVG动画按钮做得真的很酷!问题出在当前的逻辑里,点击事件只处理了动画播放,没有在动画完成后的"OPEN"状态下添加下载触发的逻辑。我来帮你调整一下代码,让它完美实现点击播放动画,再点击触发下载的功能:
关键修改点
- 新增一个状态变量
readyToDownload,用来标记按钮是否处于可下载状态 - 实现
triggerDownload函数,通过创建临时<a>标签触发浏览器的下载行为 - 修改点击事件的逻辑,区分"播放动画"和"触发下载"两种场景
修改后的完整代码
HTML
<script type='text/javascript' src="http://www.mindaffect.nl/wp-content/themes/x/js/CustomEase.min.js"></script> <script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script> <script type='text/javascript' src="http://www.mindaffect.nl/wp-content/themes/x/js/TweenMax.min.js"></script> <script type='text/javascript' src="http://www.mindaffect.nl/wp-content/themes/x/js/DrawSVGPlugin.min.js"></script> <svg viewBox='0 0 100 50' width='620' height='310' fill='none'> <circle cx='20'cy='35' r='8.5' fill='#f78c3a' class='mainCircle'></circle> <circle cx='20' cy='35' r='8.05' stroke='#f78c3a' stroke-width='.9' fill='url(#gradient)' class='mainCircleFill'></circle> <rect x='17.5' y='32.5' width='5' height='5' stroke='none' fill='#f78c3a'' class='rect'></rect> <path d='M20,39 l3.5,-3.5 l0,0 M20,39 l-3.5,-3.5 l0,0 M20,39 l0,-7.5' stroke='#303030' stroke-linecap='round' stroke-width='.8' class='arrow'></path> <text x='55' y='36.5' fill='#f78c3a'' text-anchor='middle' font-size='4.5' font-family='Lato' letter-spacing='.2' class='text'>Mindaffect Technology</text> <path d='M50,25 h30 a10,10 0 0,1 10,10 a10,10 0 0,1 -10,10 s-30,0 -60,0 a10,10 0 0,1 -10,-10 a10,10 0 0,1 10,-10 h30' stroke='#f78c3a' stroke-width='.7' fill='transparent' class='btn'></path> <circle cx='20' cy='35' r='7.9' fill='#303030' fill-opacity='0' stroke='#303030' stroke-width='1.6' stroke-opacity='0' class='subCircle'></circle> <circle cx='50' cy='26' r='0' fill='#303030' class='dot'></circle> <linearGradient id='gradient' x1='0%' y1='0%' x2='0%' y2='100%'> <stop offset='98%' class='gradient' stop-color='#f78c3a'/> <stop offset='98%' class='gradient' stop-color='#f78c3a'/> </linearGradient> </svg>
CSS
* { margin: 0; padding: 0; box-sizing: border-box; } body { min-height: 100vh; overflow: hidden; background-color: #313636; display: flex; align-items: center; justify-content: center; } svg { margin-bottom: 80px; } .btn { cursor: pointer; -webkit-tap-highlight-color: transparent; } .text { user-select: none; -webkit-font-smoothing: subpixel-antialiased; text-rendering: optimizeLegibility; } .subCircle { pointer-events: none; } .strokeW { animation: strokeW .6s forwards; @keyframes strokeW { to { stroke-width: 1.16; } } }
JavaScript
let tl, downloading = false, readyToDownload = false, // 新增状态变量:标记是否可触发下载 points = [], btn = document.querySelector('.btn'), dot = document.querySelector('.dot'), text = document.querySelector('.text'), mainCirc = document.querySelector('.mainCircle'), subCirc = document.querySelector('.subCircle'), mainCircFill = document.querySelector('.mainCircleFill'), arrow = document.querySelector('.arrow'), rect = document.querySelector('.rect'); TweenLite.set(rect, {transformOrigin: '50% 50%', rotation: 45}); btn.addEventListener('click', animation); function animation() { // 如果正在播放动画,直接返回 if (downloading) return; // 如果处于可下载状态,触发下载并重置 if (readyToDownload) { triggerDownload(); return; } downloading = !downloading; let downloadTime = Math.random() * .5 + .7; tl = new TimelineLite({onComplete: restart}); tl.restart().play() .to(arrow, .35, {y: 2.5, ease: CustomEase.create('custom', 'M0,0,C0.042,0.14,0.374,1,0.5,1,0.64,1,0.964,0.11,1,0')}, 'click') .to(text, .3, {svgOrigin: '55% 35%', scale: .77, ease: CustomEase.create('custom', 'M0,0,C0.042,0.14,0.374,1,0.5,1,0.64,1,0.964,0.11,1,0')}, 'click+=.05') .set(subCirc, {fillOpacity: 1, strokeOpacity: 1}, 'squeeze-=.3') .to(subCirc, .35, {fillOpacity: 0, ease: Power1.easeInOut}, 'squeeze-=.3') .to(subCirc, .45, {attr:{r: 13}, strokeOpacity: 0, className: '+=strokeW', ease: Power0.easeNone}, 'squeeze-=.3') .to(btn, .7, {attr:{d: 'M50,25 h0 a10,10 0 0,1 10,10 a10,10 0 0,1 -10,10 s0,0 0,0 a10,10 0 0,1 -10,-10 a10,10 0 0,1 10,-10 h0'}, ease: Sine.easeOut}, 'squeeze') .to([mainCirc, mainCircFill, rect, arrow], .7, {x: 30, ease: Sine.easeOut}, 'squeeze') .to(rect, .7, {fill: '#303030', rotation: 270, ease: Sine.easeOut}, 'squeeze') .to(text, .3, {autoAlpha: 0, y: 7, onComplete: changeText}, 'squeeze') .to(arrow, .7, {attr:{d: 'M20,39 l3.5,-3.5 l-3.5,-3.5 M20,39 l-3.5,-3.5 l3.5,-3.5 M20,39 l0,0'}, transformOrigin: '50% 50%', rotation: 225, ease: Sine.easeOut}, 'squeeze') .to(dot, .4, {attr:{r: 1.5}, ease: Back.easeOut.config(7)}) .set(subCirc, {drawSVG: 0, strokeOpacity: 1, transformOrigin: '50% 50%', x: 30, rotation: -90, attr:{r: 9.07}}) .to(subCirc, downloadTime, {drawSVG: '102%', ease: Power2.easeIn}, 'fill+=.02') .to(dot, downloadTime, {bezier:{type: 'cubic', values: points}, attr:{r: 2.7} , ease: Power2.easeIn}, 'fill') .to('.gradient', downloadTime, {attr:{offset: '0%'}, ease: Power2.easeIn}, 'fill') .to(dot, .44, {fill: '#f78c3a', y: -22, ease: Power1.easeOut}, 'stretch-=.01') .to(dot, .27, {transformOrigin: '50% 50%', scaleX: .5, ease: SlowMo.ease.config(0.1, 2, true)}, 'stretch+=.04') .to(dot, .3, {scaleY: .6, ease: SlowMo.ease.config(0.1, 2, true)}, 'stretch+=.31') .to(dot, .44, {scaleX: .4, y: 22, ease: Power2.easeIn}, 'stretch+=.45') .to([mainCirc, subCirc, arrow, rect, mainCircFill], .33, {opacity: 0, ease: Power2.easeOut}, 'stretch+=.2') .to(btn, .4, {attr:{d: 'M50,25 h20 a10,10 0 0,1 10,10 a10,10 0 0,1 -10,10 s-20,0 -40,0 a10,10 0 0,1 -10,-10 a10,10 0 0,1 10,-10 h20'}, ease: Power1.easeOut}, 'stretch+=.2') .set(dot, {opacity: 0}, 'stretch+=.875') .to(btn, .01, {stroke: '#f78c3a', ease: Power2.easeIn}, 'stretch+=.87') .to(btn, .3, {attr:{d: 'M50,25 h20 a10,10 0 0,1 10,10 a12,12 0 0,1 -10,10.5 s-20,6 -40,0 a12,12 0 0,1 -10,-10.5 a10,10 0 0,1 10,-10 h20'}, ease: CustomEase.create('custom', 'M0,0 C0.046,0.062 0.018,1 0.286,1 0.532,1 0.489,-0.206 0.734,-0.206 0.784,-0.206 0.832,-0.174 1,0')}, 'stretch+=.869') .to(text, .45, {autoAlpha: 1, y: 0, ease: Back.easeOut.config(2.5)}, 'stretch+=.855'); }; function restart() { setTimeout(() => { readyToDownload = true; // 动画完成后标记为可下载状态 downloading = false; // 这里保持文本为"OPEN",等待用户点击触发下载 }, 2000); }; function changeText() { text.textContent = 'OPEN'; TweenLite.set(text, {x: -5}); }; // 新增:触发下载的核心函数 function triggerDownload() { // 替换成你的实际下载链接和文件名 const downloadUrl = 'https://your-domain.com/your-file-to-download.zip'; const fileName = 'mindaffect-technology-file.zip'; // 创建临时a标签触发下载 const downloadLink = document.createElement('a'); downloadLink.href = downloadUrl; downloadLink.download = fileName; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); // 下载触发后重置按钮状态,回到初始样式 readyToDownload = false; tl.seek(0).pause(); text.textContent = 'MindAffect Technology'; TweenLite.set(text, {x: 0}); }; // 初始化路径点的代码保持不变 (function() { let data = Snap.path.toCubic('M0,0 a9,9 0 0,1 0,18 a9,9 0 0,1 0,-18'), dataLen = data.length; for (let i = 0; i < dataLen; i++) { let seg = data[i]; if (seg[0] === 'M') { let point = {}; point.x = seg[1]; point.y = seg[2]; points.push(point); } else { for (let i = 1; i < 6; i+=2) { let point = {}; point.x = seg[i]; point.y = seg[i+1]; points.push(point); } } } })();
代码说明
- 新增的
readyToDownload变量用来区分按钮的两个状态:动画播放中 / 可下载状态 triggerDownload函数是核心,通过创建临时<a>标签利用浏览器原生的下载机制,你只需要把downloadUrl替换成你实际的文件链接即可- 点击逻辑做了分支判断:第一次点击播放动画,动画完成后显示"OPEN",此时再次点击就会触发下载,下载完成后按钮自动回到初始状态
这样调整后,你的按钮就能完美实现动画播放 + 下载触发的完整流程啦!
内容的提问来源于stack exchange,提问作者Luuk van Driel
相关产品推荐
相关产品推荐

