问题
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?
回答1:
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
回答2:
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
回答3:
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">
来源:https://stackoverflow.com/questions/31630880/aurelia-js-how-do-i-animate-an-element-when-bound-value-changes