Skip to content

Instantly share code, notes, and snippets.

@HazelGrant
Created April 8, 2015 17:44
Show Gist options
  • Save HazelGrant/6860bfdea066a77ce1d8 to your computer and use it in GitHub Desktop.
Save HazelGrant/6860bfdea066a77ce1d8 to your computer and use it in GitHub Desktop.
Problem with SVG Path

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:

{<1>}

And here's an item when you hover over it with a mouse:

{<2>}

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:

{<4>}

and after hover:

{<6>}

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:

{<8>}

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:

{<10>}

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).

@ATMartin
Copy link

ATMartin commented Apr 8, 2015

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. :)

@HazelGrant
Copy link
Author

If there is a simpler way to do this, especially if it would help me better understand what's going on, I would love to see it. I am far more familiar with CSS than JavaScript anyway, and would greatly appreciate the reduction in complexity.

A big part of the problem here is that I'm using somebody else's code to do something I don't understand in the first place. I usually try to avoid that kind of thing and even when I do use templates (I'm not a designer, by a long shot), I like to be able to understand a template before I start using it. But in this case, I'm just falling flat.

@ATMartin
Copy link

ATMartin commented Apr 8, 2015

Take a look at this. Some WIP on it still, but general idea works fine: http://codepen.io/ATMartin/pen/LEKPda?editors=110

@HazelGrant
Copy link
Author

Oh, wow, thank you! OK, so I'm going to study this for awhile. Do you mind if I try to come up with the extractions into SASS on my own, and then ask for your insight on that? It's just, I'm completely new to preprocessors. This goes outside the scope of you doing this - which in the short term definitely solved my problem - but I'd like to do it as an exercise. Likely because I don't have much time before launch on this project, I'll be shamelessly using your provided code as a placeholder. >_> I apologize for that, and if you have any problems with me doing that I won't. But also thank you so much for providing this code pen and I'm excited to dig into it and try to extract it as a mixin.

Or attempting to finish the bounce animation... OK. This is a lot to work with and learn from. Awesome.

Actually do you mind if I bombard you with questions about this? I love that you took the time to comment things and explain your reasoning. I can go research them on my own too if you don't have a bunch of extra time. Anyway, thanks again. :)

@ATMartin
Copy link

ATMartin commented Apr 9, 2015

Of course! Fork, refactor, SASS, LESS, whatever you wanna do. That's all free-license code so I've got no problems with how you use it, haha. I'm just glad I can offer some assistance.
I'm happy to help with whatever questions you might have going forward as well. Hack that stuff up and let me know what makes sense and what needs clarifying. If there's anything I'm unsure of, we'll learn together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment