1

I'm trying to animate a button with two states: saved and unsave. When it goes from save -> unsave, it should slowly expand and add the next text. It should do the reverse when going from unsave to save. I have the following fiddle:

https://jsfiddle.net/4x0svuLd/2/

My problem right now is that even though I have designated a width transition, it seems to ignoring it. My issue is this button could have arbitrary text, thats just boolean in nature, so I can't know the exact width beforehand. I just need to it animate to the proper text width, whatever that text is.

HTML:

<div class="button" >
  <text class="text"> Save </text>
</div>

CSS:

.button {
   background-color: transparent;
   border: 1px solid;
   border-color: black;
   border-radius: 6px;
   color: black;
   cursor: pointer;
   display: inline-block;
   font-size: 14px;
   margin-top: 8px;
   outline-style: none;
   padding: 6px 10px;
   text-align: center;
   transition: width 2s cubic-bezier(0.23, 1, 0.32, 1);
   width: auto;
 }

.button-depressed {
   color: white;
   background-color: #ADD8E6;
   border-color: transparent;
}

JS:

var isSaved = false

function doSaveAnimation() {
  var button = document.getElementsByClassName("button")[0];
  button.classList.add("button-depressed");
  setTimeout(function() {
    button.innerHTML = "Unsave";
  }, 80);
}

function doUnsaveAnimation() {
  var button = document.getElementsByClassName("button")[0];
  button.classList.remove("button-depressed");
  setTimeout(function() {
    button.innerHTML = "Save";
  }, 80);
}

function animate(){
  if (isSaved) {
    doUnsaveAnimation();
  } else {
    doSaveAnimation();
  }
  isSaved = !isSaved;
}

document.getElementsByClassName("button")[0].addEventListener("click", animate);
Brandon
  • 1,029
  • 3
  • 11
  • 21
  • 1
    Transition won't animate the width property as you're not actually changing it. Width is always set to auto. – JamieC Feb 13 '16 at 16:38

2 Answers2

1

One way to do this without javascript is to use max-width instead of width.

https://jsfiddle.net/LjchoLqt/

.button {
  transition: max-width 2s cubic-bezier(0.23, 1, 0.32, 1);
  width: auto;
  max-width: 2em;
}

.button-depressed {
  max-width:4em;
}

You'll need to pay around with the values to make sure it doesn't cut off. This isnt an ideal solution though, especially if you don't control the content (eg it's populated by a CMS)

JamieC
  • 567
  • 3
  • 11
  • Hmm this seems to work! Yeah I don't control the text, but I do have a max allotted width so it should never be more than that, so this might work. Any reason the reverse animation on your fiddle doesn't trigger? – Brandon Feb 13 '16 at 17:25
  • This is because the text is changed instantly, thus changing the button width with it. – JamieC Feb 13 '16 at 17:36
  • Not quite sure I understand. The unsave animation function does the exact operation as save, just in reverse. Shouldn't it also animate? Is there anything I need to add to get it to do so? – Brandon Feb 13 '16 at 17:41
  • The max width property is still animated. But when the text changes to something shorter, the calculated width of the field changes to something much shorter than the max width, so it snaps to thay size rather than being constrained to the max width – JamieC Feb 13 '16 at 18:29
  • I haven't time to do an example, but you could basically do the reverse for when the button shrinks, i.e. set a min width and transition this. – JamieC Feb 14 '16 at 09:57
1

You could calculate the width of the text that you are setting to the button, and set calculated width to the button accordingly;
Once right away, and then each click.

  • you could also set the transition to everything, not just the width of the button - for smoother animation.

In this post you have several examples of how to determine width of text, i have adapted one of those to the solution below:

var isSaved = false;
var button = document.getElementsByClassName("button")[0];
button.style.width = getWidthOfText("Save", "14","sans-serif") +20+ "px";

function getWidthOfText(txt, fontsize) {
  var c = document.createElement('canvas');
  var ctx = c.getContext('2d');
  ctx.font = fontsize + 'px' ;
  var length = ctx.measureText(txt).width;
  return length;
}

function doSaveAnimation() {
  button.classList.add("button-depressed");
  setTimeout(function() {
    button.innerHTML = "Unsave";
    console.log(button.offsetWidth);
  }, 120);
  button.style.width = getWidthOfText("Unsave", "14") +20+ "px";
}

function doUnsaveAnimation() {

  button.classList.remove("button-depressed");
  setTimeout(function() {
    button.innerHTML = "Save";
    console.log(button.offsetWidth);
  }, 120);
  button.style.width = getWidthOfText("Save", "14") +20+ "px";
}

function animate() {
  if (isSaved) {
    doUnsaveAnimation();
  } else {
    doSaveAnimation();
  }
  isSaved = !isSaved;
}

document.getElementsByClassName("button")[0].addEventListener("click", animate);
.button {
  background-color: transparent;
  border: 1px solid;
  border-color: black;
  border-radius: 6px;
  color: black;
  cursor: pointer;
  display: inline-block;
  font-size: 14px;
  margin-top: 8px;
  outline-style: none;
  padding: 6px 10px;
  text-align: center;
  transition: 1s cubic-bezier(0.23, 1, 0.32, 1);
  text-align:center;
}
.button-depressed {
  color: white;
  background-color: #ADD8E6;
  border-color: transparent;
  transition: 1s cubic-bezier(0.23, 1, 0.32, 1);
}
<div class="button">
  <text class="text">Save</text>
</div>
Community
  • 1
  • 1
Banana
  • 7,424
  • 3
  • 22
  • 43
  • @BrandonM you are most welcomed. note the `+20` which i have added to the sizing, it reflects the left + right padding of the button text as was set in your css. it makes the button sizing more precise. – Banana Feb 13 '16 at 19:57