Attempt to re-create CJ Gammon’s portfolio grid hover effect using SVG clip-path and CSS Transitions.
A Pen by Noel Delgado on CodePen.
:ruby | |
@items = [ | |
{ :t => 'X-rays', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-xrays.png' }, | |
{ :t => 'Worms', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-worms.png' }, | |
{ :t => 'Aurora', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-aurora.png' }, | |
{ :t => 'Angus', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-angus.png' }, | |
{ :t => 'Huitzi', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-huitzi.png' }, | |
{ :t => 'Dalí', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-dali.png' }, | |
{ :t => 'The Bride', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-bride.png' }, | |
{ :t => 'The Man', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-man.png' }, | |
{ :t => 'D', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-d.png' }, | |
{ :t => 'V', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-v.png' }, | |
{ :t => 'V II', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-v2.png' }, | |
{ :t => 'V III', :i => 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/9473/i-v3.png' } | |
] | |
.container | |
%header | |
%h1 SVG clip-path Hover Effect | |
%p | |
Attempt to re-create <a href="http://www.cjgammon.com/" target="_blank">CJ Gammon’s</a> | |
portfolio grid hover effect using SVG and CSS Transitions. | |
%p.small | |
<b>Note:</b> this is an experiment, it does not seem to work on Firefox 43.0.4 | |
neither have touch support. | |
<br/>Tested on Chrome 47.0.2526.106, Opera 34.0 and Safari 8.0.6. | |
%main | |
.items | |
- @items.each_with_index do | item, index | | |
.item | |
%svg{:viewBox => "0 0 300 200", :preserveAspectRatio => "xMidYMid slice"} | |
%defs | |
%clipPath{:id => "clip-#{index}"} | |
%circle{:cx => "0", :cy => "0", :r => "150px", :fill => "#000"} | |
%text{:class => "svg-text", :x => "50%", :y => "50%", :dy => ".3em"} | |
#{item[:t]} | |
%g{:"clip-path" => "url(#clip-#{index})"} | |
%image{"xlink:href" => "#{item[:i]}", :width => "100%", :height => "100%", :preserveAspectRatio => "xMinYMin slice"} | |
%text{:class => "svg-masked-text", :x => "50%", :y => "50%", :dy => ".3em"} | |
#{item[:t]} | |
.options | |
%button.dark | |
%button.light |
/* | |
* Noel Delgado | @pixelia_me | |
*/ | |
var items = [] | |
, point = document.querySelector('svg').createSVGPoint(); | |
function getCoordinates(e, svg) { | |
point.x = e.clientX; | |
point.y = e.clientY; | |
return point.matrixTransform(svg.getScreenCTM().inverse()); | |
} | |
function changeColor(e) { | |
document.body.className = e.currentTarget.className; | |
} | |
function Item(config) { | |
Object.keys(config).forEach(function (item) { | |
this[item] = config[item]; | |
}, this); | |
this.el.addEventListener('mousemove', this.mouseMoveHandler.bind(this)); | |
this.el.addEventListener('touchmove', this.touchMoveHandler.bind(this)); | |
} | |
Item.prototype = { | |
update: function update(c) { | |
this.clip.setAttribute('cx', c.x); | |
this.clip.setAttribute('cy', c.y); | |
}, | |
mouseMoveHandler: function mouseMoveHandler(e) { | |
this.update(getCoordinates(e, this.svg)); | |
}, | |
touchMoveHandler: function touchMoveHandler(e) { | |
e.preventDefault(); | |
var touch = e.targetTouches[0]; | |
if (touch) return this.update(getCoordinates(touch, this.svg)); | |
} | |
}; | |
[].slice.call(document.querySelectorAll('.item'), 0).forEach(function (item, index) { | |
items.push(new Item({ | |
el: item, | |
svg: item.querySelector('svg'), | |
clip: document.querySelector('#clip-'+index+' circle'), | |
})); | |
}); | |
[].slice.call(document.querySelectorAll('button'), 0).forEach(function (button) { | |
button.addEventListener('click', changeColor); | |
}); |
$db: #2f3238; | |
$dc: #f5f5f5; | |
$lb: #f9f9f9; | |
$lc: #1a1a1a; | |
$l: #1abc89; | |
* {margin: 0; box-sizing: border-box;} | |
:root { | |
font-size: 13px; | |
font-family: 'Source Sans Pro', sans-serif; | |
line-height: 1.618; | |
font-weight: 400; | |
} | |
body { | |
background-color: $db; | |
color: $dc; | |
} | |
a {color: $l} | |
a:hover {opacity: .8} | |
p { | |
font-size: 1.2rem; | |
color: rgba($dc, .5); | |
} | |
.small { | |
font-size: 1rem; | |
margin-top: 1em; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 4rem 2rem; | |
} | |
header { | |
text-align: center; | |
padding-bottom: 3rem; | |
} | |
h1 { | |
font-size: 2.6rem; | |
line-height: 1.2em; | |
padding-bottom: 1rem; | |
font-weight: 600; | |
} | |
svg { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
} | |
circle { | |
transform-origin: 50% 50%; | |
transform: scale(0); | |
transition: transform 200ms cubic-bezier(0.250, 0.460, 0.450, 0.940); | |
} | |
text { | |
font-size: 1.1rem; | |
text-transform: uppercase; | |
text-anchor: middle; | |
letter-spacing: 1px; | |
font-weight: 600; | |
} | |
.svg-text {fill: lighten($db, 16)} | |
.svg-masked-text {fill: rgba(#fff, 1);} | |
image { | |
transform: scale(1.1); | |
transform-origin: 50% 50%; | |
transition: transform 200ms cubic-bezier(0.250, 0.460, 0.450, 0.940); | |
} | |
.items { | |
display: flex; | |
flex-flow: row wrap; | |
justify-content: center; | |
} | |
.item { | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
position: relative; | |
overflow: hidden; | |
width: 300px; | |
height: 200px; | |
margin: 5px; | |
cursor: pointer; | |
background-color: lighten($db, 5); | |
border-radius: 2px; | |
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.02), inset 0 0px 0px 1px rgba(0, 0, 0, 0.07); | |
transform: translateZ(0); | |
} | |
.item:hover { | |
circle, | |
image {transform: scale(1)} | |
} | |
button { | |
width: 12px; | |
height: 12px; | |
border: none; | |
appearance: none; | |
box-shadow: 0 0 0 1px rgba(0,0,0,.5); | |
border-radius: 1px; | |
&.dark {background-color: $db;} | |
&.light {background-color: $lb;} | |
} | |
.options { | |
position: absolute; | |
top: 1rem; | |
right: 1rem; | |
button {margin-left: .5rem} | |
} | |
.light { | |
background-color: $lb; | |
color: $lc; | |
p {color: rgba($lc, .5);} | |
.item {background: $dc;} | |
.svg-text {fill: rgba(#000, .1);} | |
} |
Attempt to re-create CJ Gammon’s portfolio grid hover effect using SVG clip-path and CSS Transitions.
A Pen by Noel Delgado on CodePen.