I am working with a WrapBootstrap template, called Restart, in a Rails 4 application, which has a portfolio page where, when you hover over an item (which is a picture), a little gray box with a slanted bottom drops down over the picture with some text.
Here's an item, as-is:
And here's an item when you hover over it with a mouse:
Before I go any further, I'll say this: I had absolutely no idea how this worked. I've been translating the template into the Rails Asset Pipeline with no problem up to this point. I'd never heard of SVG before delving into this problem and so I'll probably get verbage and vocabular wrong while trying to describe it.
Onwards. So, the HTML to make this work in the template is here:
<div class="portfolio_item">
<a href="portfolio_item.html" data-path-hover="M 180,190 0,158 0,0 180,0 z">
<figure style="background-image:url(images/portfolio/p4.jpg)">
<svg viewBox="0 0 180 320" preserveAspectRatio="none">
<path d="M 180,0 0,0 0,0 180,0 z"/>
</svg>
<figcaption>
<p>
Description of the project dapibus, tellus ac cursus commodo, mauesris condime ntum nibh, ut fermentum....
</p>
<div class="view_button">View</div>
</figcaption>
</figure>
</a>
<div class="portfolio_description">
<h3>
<a href="portfolio_item.html">Lorem Ipsum</a>
</h3>
<p>people</p>
</div>
The files required for this are:
- snap.svg-min.js
- restart_theme.js
- style.css.scss
These are all included properly in the page. (I'll get into what I think they're doing in a minute.)
So the problem is that, on my own webpage, I have the following code (using an ERB partial that renders from a collection of objects that have images):
<div class="portfolio_item">
<a href="<%= video['url'] %>" target="_blank" data-path-hover="M 180,190 0,158 0,0 180,0 z">
<figure style="background-image: url(<%= video['image'] %>)">
<svg viewBox="0 0 180 320" preserveAspectRatio="none">
<path d="M 180,0 0,0 0,0 180,0 z" />
</svg>
<figcaption>
<p>
<%= raw video['abstract'].truncate(100) if video['abstract'] %>
</p>
<div class="view_button">
View
</div>
</figcaption>
</figure>
</a>
<div class="portfolio_description">
<h3>
<a href="<%= video['url'] %>" target="_blank">
<%= video['title'].truncate(40) %>
</a>
</h3>
<p>
<% video['presenters'].each do |presenter| %>
<%= presenter['first_name'] %> <%= presenter['last_name'] %>
<% end %>
</p>
</div><!-- /.portfolio_description -->
</div><!-- /.portfolio_item -->
And this shows up on my page, before hover:
and after hover:
No nifty gray box with a slanting bottom. :(
So. With my initial research, I found that Snap.svg is a JavaScript library that animates SVG images, that the SVG path in this file is telling a line to be drawn across the image from one point (the data-path-hover
to the path d=
) and then filling in the space with gray and making space opaque (somehow without making the text also opaque, which leads me to believe it's an absolutely positioned element of its own).
The code in the restart_theme.js file that points to this and, I believe, activates the snap.svg-min.js code, is here (you can probably just skim the next two code snippets briefly - I don't think the problem is here):
(function() {
function init() {
var speed = 330,
easing = mina.backout;
[].slice.call ( document.querySelectorAll( '.portfolio_item > a' ) ).forEach( function( el ) {
var s = Snap( el.querySelector( 'svg' ) ), path = s.select( 'path' ),
pathConfig = {
from : path.attr( 'd' ),
to : el.getAttribute( 'data-path-hover' )
};
el.addEventListener( 'mouseenter', function() {
path.animate( { 'path' : pathConfig.to }, speed, easing );
} );
el.addEventListener( 'mouseleave', function() {
path.animate( { 'path' : pathConfig.from }, speed, easing );
} );
} );
}
And then there's a call to init
the entire file (which has code for initializing a couple other JavaScript libraries such as prettyPhoto, etc.).
I've found the CSS for the entire process in the style.css.scss file:
.portfolio_strict .portfolio_item {
text-align: center;
padding: 0px;
height: 320px;
overflow: hidden;
margin-bottom: 24px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: all 500ms linear;
transition: all 500ms linear;
}
.portfolio_strict .portfolio_item a:hover figcaption h2 {
transform: translateY(20px);
-ms-transform: translateY(20px);
-webkit-transform: translateY(20px);
color: #fff;
}
.portfolio_strict .portfolio_item a:hover figcaption p {
transition-timing-function: ease-out;
-webkit-transition-timing-function: ease-out;
transform: translateY(0px);
-ms-transform: translateY(0px);
-webkit-transform: translateY(0px);
opacity: 1;
transition-duration: 0.3s;
-ms-transition-duration: 0.3s;
-webkit-transition-duration: 0.3s;
transition: opacity 0s, transform 0.3s;
transition-delay: 0.05s;
-ms-transition-delay: 0.05s;
-webkit-transition-delay: 0.05s;
color: #fff;
font-weight: 400;
}
.portfolio_strict .portfolio_item a:hover figure .view_button {
transition-timing-function: ease-out;
-webkit-transition-timing-function: ease-out;
transform: translateY(0);
-webkit-transform: translateY(0);
}
.portfolio_strict .portfolio_item figure {
height: 230px;
position: relative;
overflow: hidden;
margin: 0px;
background: #fff;
background: no-repeat center center;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
border: solid 0px #fff;
}
.portfolio_strict .portfolio_item figure svg {
position: absolute;
top: -1px;
/* fixes rendering issue in FF */
left: 0px;
z-index: 10;
width: 100%;
height: 100%;
}
.portfolio_strict .portfolio_item figure svg path {
fill: #000;
fill-opacity: 0.5;
-webkit-transition: all 500ms linear;
transition: all 500ms linear;
}
.portfolio_strict .portfolio_item figure figcaption {
position: absolute;
top: 0;
z-index: 11;
padding: 0px;
width: 100%;
height: 100%;
text-align: center;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
.portfolio_strict .portfolio_item figure figcaption h2 {
margin: 0px 0 20px 0;
color: #fff;
font-weight: 300;
font-size: 15px;
transition: transform 0.3s;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
timing-function: cubic-bezier(0.25, 0.25, 0.115, 1.445);
-webkit-animation-timing-function: cubic-bezier(0.25, 0.25, 0.115, 1.445);
}
.portfolio_strict .portfolio_item figure figcaption p {
padding: 10px 20px;
color: #aaa;
font-weight: 300;
font-size: 13px;
transform: translateY(-10px);
-webkit-transform: translateY(-10px);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
timing-function: cubic-bezier(0.25, 0.25, 0.115, 1.445);
-webkit-animation-timing-function: cubic-bezier(0.25, 0.25, 0.115, 1.445);
opacity: 0;
}
.portfolio_strict .portfolio_item figure figcaption p.safari {
background-color: rgba(0, 0, 0, 0.5);
}
.portfolio_strict .portfolio_item figure figcaption .view_button {
position: absolute;
padding: 4px 20px;
border: none;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: bold;
-webkit-transition: -webkit-opacity 0.3s, -webkit-transform 0.3s;
transition: opacity 0.3s, transform 0.3s;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
bottom: 0;
left: 0;
padding: 15px;
width: 100%;
background: #428bca;
color: #fff;
font-weight: 300;
transform: translateY(100%);
-webkit-transform: translateY(100%);
timing-function: cubic-bezier(0.25, 0.25, 0.115, 1.445);
-webkit-animation-timing-function: cubic-bezier(0.25, 0.25, 0.115, 1.445);
}
.portfolio_strict .portfolio_item .portfolio_description {
padding: 20px 0 14px 0px;
margin-bottom: 20px;
border-bottom: solid 1px #ccc;
}
.portfolio_strict .portfolio_item .portfolio_description h3 {
margin: 0;
padding: 0 0 6px 0;
font-size: 15px;
line-height: 20px;
font-weight: 600;
}
.portfolio_strict .portfolio_item .portfolio_description p {
margin: 0;
padding: 0;
font-size: 10px;
color: #888888;
text-transform: uppercase;
letter-spacing: 1px;
}
After a little bit of research, and with the knowledge that the portfolio.html file within the WrapBootstrap template itself is working perfectly, I believe that this is all in working order. I'm showing the code for completeness.
However, there's a difference between the way the HTML code is rendering between the two pages that leads me to believe that what's happening here is a problem with the JavaScript not interacting properly with the SVG path.
Here's a snapshot of the relevant code in portfolio.html before hover:
The path d
string is different! I have no idea what's going on. The path d
string also changes dynamically when I hover over the image.
So, I decided to get clever and pasted the changed path d
string after hover into my HTML, to see if the trouble was rendering the SVG image itself, or something with the JavaScript. So I changed my code to this:
<svg viewBox="0 0 180 320" preserveAspectRatio="none">
<path d="M180,190C180,190,0,158,0,158C0,158,0,158,0,0,0,0C0,0,180,0,180,0C180,0,180,190,180,190" />
</svg>
Which is what the code shows in portfolio.html when hovering over the image. This produced:
This is without hovering over the image. So I can get the SVG path image to show up. On hover, the text drops down the same as before, with no change to the SVG image.
So the problem has got to be with the way the JavaScript is interacting with the SVG, right? And this is a separate layer from the text, which I believe is actually just a simple matter of CSS :hover
and changing the state of the text from hidden
to visible
.
My JavaScript files are being included in my Rails application in the same order as they're included in portfolio.html. There are no console errors.
The HTML in my Rails app on inspect element
shows up exactly the same way it does within my text editor - the path d
string is not being chaged or manipulated the way it's supposed to.
I have the same results in both Chrome and Firefox. I don't know a whole lot about debugging JavaScript, and my attempts at trying to figure out if this is an Asset Pipeline problem are running dry. I know I can render the SVG properly in Rails. I know that JavaScript from the same file as the event listeners for the SVG image is working perfectly.
This problem is making me want to scratch my eyes out. >_> Thank you for offering to help. I know this is long but I'm not even sure I've included everything that I've tried (I'm trying to remember everything I tried yesterday and this is what I can recreate this morning).
So I've just breezed over this between meetings, and I'm certain I've missed a lot of things, but my first thought is that trying to do what you want to using SVG & JS animation libraries is going way too deep into the weeds. You can get that same effect using a CSS :Hover state & simple animations without ever touching the Javascript (except for a click listener on that View button).
Before I put the time into a Codepen as a demo this evening: are you okay doing this with CSS? I see from the live preview that they're using SVG, and there's certainly nothing wrong with it, but I think you're already running afoul of the complexity issues it can introduce. :)