Reputation: 39
I have a flashy download button for my domain using DrawSVG, CustomEasy and TweenMax. Yet, I can not seem to figure out how to get the download triggered.
The button opens up an animation when clicked. Eventually, the OPEN text appears at the end of the animation, where it awaits the click of the user. How can I best implement a download onclick (on the OPEN)? I have tried many on.click events and other implementations, but I can not get it to work. I am forever grateful for your help.
https://codepen.io/anon/pen/qpXRdm
let tl, downloading = 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;
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(() => {
tl.seek(0).pause();
text.textContent = 'MindAffect Technology';
TweenLite.set(text, {x: 0});
downloading = false;
}, 2000);
};
function changeText() {
text.textContent = 'OPEN';
TweenLite.set(text, {x: -5});
};
(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);
}
}
}
})();
* {
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;
}
}
}
<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>
Kind regards
Upvotes: 0
Views: 158
Reputation: 177
I modified your example for it to be able to work with multiple buttons in the same page.
First we need to set up the SVG document like this:
<svg viewBox='0 0 100 50' width='620' height='310' fill='none' class="svg-button" download-url="https://static.pexels.com/photos/784927/pexels-photo-784927.jpeg">
...
</svg>
Notice we are adding the .svg-button
class, and the file download url in the download-url
attribute.
We will be moving all of your variable declarations inside the animation()
function, as well as your changeText()
function. For this point check the Codepen for reference. View CodePen example here
We then need to remove btn.addEventListener("click", animation())
from your JS. We will delegate the click
event to the document
instead:
document.addEventListener("click", function(event){
if (event.target.classList.contains("svg-button") {
console.log("target", event.target);
animation(event.target)
}
});
Next, we will create a click
event listener for opening the URL when your timeline ends, using an onComplete
callback like this:
tl = new TimelineLite({
onComplete: function(){
target.addEventListener("click", function(){
Object.assign(document.createElement('a'), {
target: '_blank',
href: target.getAttribute("download-url") }).click();
})
}
});
We set pointer-events: none;
for all the elements inside your .svg-button
like this, so that the event doesn't propagate to the inner elements:
.svg-button {
margin-bottom: 80px;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
* {
pointer-events: none;
}
}
Upvotes: 1
Reputation: 141
Currently the 'onComplete' callback for the button is set to 'restart'.
tl = new TimelineLite({onComplete: restart});
You could instead pass a function that adds a click handler instead of restarting the animation.
tl = new TimelineLite({onComplete: allowOpen});
and define the function to add a click handler:
function allowOpen(){
btn.removeEventListener('click',animation');
btn.addEventListener('click',open);
}
function open () {
alert('opened!');
}
Upvotes: 0
Reputation: 4412
hack it:
function changeText() {
text.textContent = 'OPEN';
btn.removeEventListener('click', animation);
btn.addEventListener('click', function() { alert('hey!'); });
TweenLite.set(text, {x: -5});
};
but then ask yourself: do you really want the user to have to make another click?
Upvotes: 0