简体   繁体   中英

Aurelia.js: How do I animate an element when bound value changes?

I am using Aurelia.js for my UI. Let's say I have the following view markup:

<tr repeat.for="item in items">
    <td>${item.name}</td>
    <td>${item.value}</td>
</tr>

Which is bound to a model "items". When one of the values in the model changes, I want to animate the cell where the changed value is displayed. How can I accomplish this?

This can be done with Aurelia custom attributes feature.

Create a new javascript file to describe the attribute (I called the attribute "animateonchange"):

import {inject, customAttribute} from 'aurelia-framework';
import {CssAnimator} from 'aurelia-animator-css';

@customAttribute('animateonchange')
@inject(Element, CssAnimator)
export class AnimateOnChangeCustomAttribute {
    constructor(element, animator) {
        this.element = element;
        this.animator = animator;
        this.initialValueSet = false;
    }

    valueChanged(newValue){
        if (this.initialValueSet) {
            this.animator.addClass(this.element, 'background-animation').then(() => {
                this.animator.removeClass(this.element, 'background-animation');
            });
        }
        this.initialValueSet = true;
    }
}

It receives the element and CSS animator in constructor. When the value changes, it animates the element with a predefined CSS class name. The first change is ignored (no need to animate on initial load). Here is how to use this custom element:

<template>
    <require from="./animateonchange"></require>
    <div animateonchange.bind="someProperty">${someProperty}</div>
</template>

See the complete example in my blog or on plunkr

The creator of the crazy Aurelia-CSS-Animator over here :)

In order to do what you want you simply need to get hold of the DOM-Element and then use Aurelia's animate method. Since I don't know how you're going to edit an item, I've just used a timeout inside the VM to simulate it.

attached() {
  // demo the item change
  setTimeout( () => {
    let editedItemIdx = 1;

    this.items[editedItemIdx].value = 'Value UPDATED';
    console.log(this.element);
    var elem = this.element.querySelectorAll('tbody tr')[editedItemIdx];

    this.animator.addClass(elem, 'background-animation').then(() => {
      this.animator.removeClass(elem, 'background-animation')
    });
  }, 3000);
}

I've created a small plunkr to demonstrate how that might work. Note this is an old version, not containing the latest animator instance, so instead of animate I'm using addClass/removeClass together.

http://plnkr.co/edit/7pI50hb3cegQJTXp2r4m

Also take a look at the official blog post, with more hints http://blog.durandal.io/2015/07/17/animating-apps-with-aurelia-part-1/

Hope this helps

Unfortunately the accepted answer didnt work for me, the value in display changes before any animation is done, it looks bad.

I solved it by using a binding behavior, the binding update is intercepted and an animation is applied before, then the value is updated and finally another animation is done. Everything looks smooth now.

    import {inject} from 'aurelia-dependency-injection';  
import {CssAnimator} from 'aurelia-animator-css';



@inject(CssAnimator)
export class AnimateBindingBehavior {

  constructor(_animator){
    this.animator = _animator;
  }

  bind(binding, scope, interceptor) {

    let self = this;

    let originalUpdateTarget =  binding.updateTarget;

    binding.updateTarget = (val) => {
      self.animator.addClass(binding.target, 'binding-animation').then(() => {
        originalUpdateTarget.call(binding, val);
        self.animator.removeClass(binding.target, 'binding-animation')
      });
    }
  }

  unbind(binding, scope) {
    binding.updateTarget = binding.originalUpdateTarget;
    binding.originalUpdateTarget = null;
  }
}

Declare your animations in your stylesheet:

@keyframes fadeInRight {
  0% {
    opacity: 0;
    transform: translate3d(100%, 0, 0);
  }
  100% {
    opacity: 1;
    transform: none
  }
}


@keyframes fadeOutRight {
  0% {
    opacity: 1;
    transform: none;
  }
  100% {
    opacity: 0;
    transform: translate3d(-100%, 0, 0)
  }
}


    .binding-animation-add{
      animation: fadeOutRight 0.6s;
    }

    .binding-animation-remove{
      animation: fadeInRight 0.6s;
    }

You use it in your view like

<img src.bind="picture & animate">

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM