Grolsch 400: Building a graphic-shifting hover state with SVG
At Nation we were asked to display the artworks of 400 artists to celebrate 400 years of Grolsch. You can see the mini site over here - Grolsch 400. Sadly this site is no longer live, it was quite a long time ago now.
During this build, a few things stood out as ‘niceties’ to me which I will go through in a bit of detail; one of them being the "Explore the art gallery" button.
It looks like an obvious choice to go through exactly how the artwork grid works, but half way through building that grid, there were some unexpected bumps in the road and some of those bumps I didn’t have time to fully get my head around. Something something transform scale / transform translate something something. That part of the site ended up needing some extra attention to meet the 400-year deadline 😰
This had to work in Internet Explorer 9, so it was important to find a solution that would work cross-browser. Above is the SVG solution I went with, and here is the code for that:
<div class="explore">
<a href="#">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" class="button">
<defs>
<pattern id="background" patternUnits="userSpaceOnUse" width="469" height="1991" >
<image xlink:href="explore-cta.jpg" class="image" width="469" height="1991"></image>
</pattern>
</defs>
<text fill="#6ec049" x="35" y="50">Explore the art gallery</text>
</svg>
<svg class="border border-top">
<rect fill="#6ec049" width="100%" height="100%"></rect>
</svg>
<svg class="border border-right">
<rect fill="#6ec049" width="100%" height="100%"></rect>
</svg>
<svg class="border border-bottom">
<rect fill="#6ec049" width="100%" height="100%"></rect>
</svg>
<svg class="border border-left">
<rect fill="#6ec049" width="100%" height="100%"></rect>
</svg>
</a>
</div>
Each element inside our button (borders and text) has a starting fill of #6ec049
, then on 'mouseover', this fill is set to use our <pattern>
we have defined inside the <defs>
tag.
The <pattern>
we defined contains the image that we will be flicking through on hover. This is essentially a sprite sheet. Here is it slowed down and animated so we can see what is actually happening 👇
button example here
At the same time the new fill is set on 'mouseover', we start an interval to display each frame. I think what you can also see in the animation is a miscalculation of frame heights on the jpg, you didn't see that 😬
Here is the interval code:
__LINK.addEventListener('mouseover', function(e) { startCtaEffect(e); });
function startCtaEffect() {
setCtaFill();
ctaFrameInterval = setInterval(function() {
// Increment ctaCurrentFrame
ctaCurrentFrame++;
// Move to the next frame based on
// the ctaCurrentFrame index
ctaMoveFrame();
}, ctaFrameRate);
}
function setCtaFill() {
// Only store one of the elements' fill, these colours
// should always stay the same
originalFill = __LINK.querySelector('text').getAttribute('fill');
// Set new fill, which is the svg pattern #background id
var i = 0, borders = __LINK.querySelectorAll('svg rect');
for (; i < borders.length; i++) {
borders[i].setAttribute('fill', 'url(#background)');
}
__LINK.querySelector('svg text').setAttribute('fill', 'url(#background)');
}
...then finally we move the image:
function ctaMoveFrame() {
// Set top to 0
var newTop = 0;
// If we are at the end of the frames
// set the current frame back to the beginning
if (ctaCurrentFrame === ctaTotalFrames) {
ctaCurrentFrame = 0;
// ...otherwise, continue by multiplying the
// ctaCurrentFrame index with the frameHeight
} else {
newTop = ctaCurrentFrame * ctaFrameHeight;
}
// Set newTop at each interval
var value = ' translate(0, -' +newTop+ ')';
__LINK.querySelector('.image').setAttribute('transform', value);
}
The transform
attribute is being set on the svg image
tag, not a CSS transform. Just something to note, super nice article about SVG transforms over here.
Want to tell me how much you hate my solution and that you want me to stop doing front-end as a full-time job – @tomsansome ✋🤖