Come animare gli oggetti ng-repeat relativi agli eventi click che causano la modifica

Sto cercando di animare un utente selezionando elementi da diversi gruppi di elementi. L’elemento dovrebbe animare dal set cliccato alla sua nuova posizione nell’elenco degli elementi selezionati.

Nella demo sottostante, considera le caselle rosa come elementi disponibili e la casella con bordi come l’elenco degli elementi selezionati (caselle blu). L’utente può selezionare un elemento facendo clic su una delle caselle rosa:

angular.module('test', ['ngAnimate']) .controller('testCtrl', function($scope) { $scope.products = [{}, {}, {}, {}]; $scope.purchased = [{}]; $scope.purchase = function(dir) { $scope.direction = dir $scope.purchased.push($scope.products.pop()); }; }) .directive('testDir', function($animate) { return { link: function(scope, element) { $animate.on('enter', element, function(element, phase) { $target = scope.direction == 'left' ? $('.stock:first') : $('.stock:last'); element.position({ my: 'center', at: 'center', of: $target, using: function(pos, data) { $(this).css(pos); $(this).animate({ top: 0, left: 0 }); } }); }); } }; }); 
 .stock { display: inline-block; width: 50px; height: 50px; background: hotpink; } .stock.right { margin-left: 100px; } .product { height: 50px; width: 50px; border: 1px solid; } .purchased { height: 60px; margin-top: 100px; border: 2px dotted; } .purchased .product { display: inline-block; margin: 5px; background: dodgerblue; } 
     


Beh, funziona in qualche modo – ma sto usando jQuery-ui per scoprire la posizione di partenza (la posizione delle scatole rosa sarà cauta in un design reattivo) e il metodo jquery animate per animare l’elemento.

Inoltre devo memorizzare la direzione cliccata in ambito e sto impostando sia la posizione iniziale che l’animazione alla posizione finale nel listener di eventi enter .

Ho letto e sperimentato molto con ganci di animazione incorporati in angular, ma non sono riuscito a trovare un modo corretto per animare elementi da posizioni relative / dinamiche.

C’è un modo migliore per ottenere la stessa esperienza utente in modo angular js ..?

se ho capito bene la tua domanda (dimmi se no); Penso che un modo per gestire il problema, va in questo modo:

pur assumendo che le dimensioni (larghezza) dei tuoi prodotti siano costanti -set a 50px o qualcosa-; puoi impostare la posizione degli elementi rosa in assoluto; quindi usa ng-repeat per gli elementi rosa, con un breve attributo stile ng all’interno dell’html come questo:

 

e sui prodotti acquistati: invece di usare ng-repeat sull’array “acquistato”, all’interno della funzione “add-to-bought”, dopo aver spinto il prodotto nell’array “acquistato”, puoi semplicemente animare il prodotto sul “top” : “la distanza in altezza dell’elemento confinante” e “sinistra” è uguale a {$ scope.purchased.length * 50 + ‘px’}. quindi aggiungi una class usando ng-class (con un interruttore) per colorare e altre cose in css … (puoi anche considerare la transizione per i cambiamenti di colore, come probabilmente sai)

Penso anche che sia ansible gestire diversi problemi di altezze e top (nel caso in cui il numero di prodotti diventi più di una linea) con una class ng che aggiunge classi con nuovi valori “top” basati su: ($ index> some- numero), e un’altra class ng per l’elemento superiore (l’elemento che si trova sopra l’elemento bordato), cambiando l’altezza …

spero che questo sia stato utile


Aggiornare:

sfortunatamente non avevo capito bene la domanda. ma guardando il problema ora, penso che ci sia un modo per farlo in modo più dinamico.

all’interno della funzione $scope.purchase , puoi $scope.purchase messaggio alla tua direttiva con $broadcast e passando l’elemento cliccato come questo (per ogni elemento in stock, o creato con ng-repeat o meno):

 

e:

 $scope.purchase = function(event) { $scope.purchased.push($scope.products.pop()); $scope.$broadcast('purchaseHappened', event.target); }; 

e all’interno della tua direttiva, metti il ​​listener di eventi:

 scope.$on('purchaseHappened', function(event, target) { //catch target in here, and then use it's position to animate the new elements... }) 

Penso che tu possa usare anche target.getBoundingClientRect() per ottenere la posizione dell’elemento, relativa al viewport ( .left , .left , …) invece di jquery-ui’s .position se vuoi …

è più vicino alla soluzione?

Questa soluzione è un miglioramento in quanto elimina la necessità di aggiungere informazioni all’ambito nella funzione di acquisto ed evita di mescolare i dati del modello e i dettagli dell’interfaccia utente utilizzando una direttiva “sorgente” e memorizzando le informazioni di origine in una proprietà del controllore. L’esempio è semplificato e può, naturalmente, essere migliorato. Il punto chiave è che i dati richiesti per gestire il processo non vengono mai esposti tramite l’ambito.

Se l’elemento target è sobject alla rimozione dal DOM (ovvero fa parte di una ng-repeat, allora questa soluzione dovrebbe essere leggermente modificata per calcolare e memorizzare le posizioni di inizio dell’animazione come parte del gestore monitor.click piuttosto che memorizzare l’elemento objective stesso.

Il metodo di animazione mi sembra arbitrario. Questo esempio utilizza il metodo di animazione jqueryUI dell’OP, ma funzionerebbe altrettanto bene con le transizioni css o usando $ animate.

Un esempio completo è qui

 angular.module('app', []) .controller('main', function($scope) { $scope.products = [{},{}]; $scope.purchased = [{}]; $scope.Purchase = function() { $scope.purchased.push({}); }; }) .directive('source', function(){ return { controller: function($scope) { } }; }) .directive('originator', function(){ return{ require: '^source', priority: 1, link:{ pre: function(scope, element, attr, ctrl){ element.on('click', function(evt){ ctrl.target = evt.target; }); } } }; }) .directive('sink', function(){ return { require: '^source', link: function(scope, element, attr, ctrl){ var target = ctrl.target; if(target){ var $target = $(target); //animate from target to current position element.position({ my: 'center', at: 'center', of: $target, using: function(pos, data) { $(this).css(pos); $(this).animate({ top: 0, left: 0 }); } }); ctrl.target = undefined; } } }; });