javascript - lite - Cómo animar elementos ng-repeat relativos a los eventos de clic que causan el cambio
jquery in angularjs (2)
Intento animar a un usuario seleccionando elementos de diferentes conjuntos de elementos. El elemento debe animar desde el conjunto cliqueado a su nueva posición en la lista de elementos seleccionados.
En la demostración siguiente, considere los cuadros de color rosa como elementos disponibles y el cuadro delimitado como la lista de elementos seleccionados (recuadros azules). El usuario puede seleccionar un elemento haciendo clic en cualquiera de los cuadros de color 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;
}
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<script src="https://code.angularjs.org/1.4.8/angular-animate.js"></script>
<div ng-app="test" ng-controller="testCtrl">
<div class="stock" ng-click="purchase(''left'')"></div>
<div class="stock right" ng-click="purchase(''right'')"></div>
<div class="purchased clearfix">
<div class="product" ng-repeat="product in purchased" data-test-dir>
</div>
</div>
</div>
Bueno, funciona, pero estoy usando jQuery-ui para descubrir la posición de partida (la posición de los recuadros rosa será cautelosa en un diseño receptivo) y el método animado jquery para animar el elemento.
También tengo que almacenar la dirección cliqueada en el alcance y estoy configurando tanto la posición inicial como la animación hasta la posición final en el oyente del evento enter
.
He estado leyendo y experimentando mucho con ganchos de animación integrados en angular, pero no he podido encontrar una forma adecuada de animar elementos desde posiciones relativas / dinámicas.
¿Hay una mejor manera de lograr la misma experiencia de usuario en angular js way ..?
Esta solución es una mejora ya que elimina la necesidad de agregar información al alcance en la función de Compra y evita mezclar los datos del modelo y los detalles de UI mediante el uso de una directiva "fuente" y el almacenamiento de la información de origen en una propiedad del controlador. El ejemplo se simplifica y, por supuesto, se puede mejorar. El punto clave es que los datos requeridos para gestionar el proceso nunca se exponen a través del alcance.
Si el elemento objetivo está sujeto a ser eliminado del DOM (es decir, es parte de una repetición ng), esta solución debería modificarse ligeramente para calcular y almacenar las posiciones de inicio de la animación como parte del monitor. Haga clic en el controlador en lugar de en la tienda el elemento objetivo en sí.
El método de animación me parece arbitrario. Este ejemplo usa el método de animación jqueryUI de OP, pero funcionaría igual de bien con las transiciones css o usando $ animate.
Un ejemplo completo está here
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;
}
}
};
});
si entendí tu pregunta correctamente (dime si no); Creo que una forma de manejar el problema es así:
asumiendo que el tamaño (ancho) de sus productos sea constante, ajuste a 50px o algo así; puedes establecer la posición de los elementos rosados en absoluta; luego use ng-repeat para los elementos de color rosa, con un breve atributo ng-style dentro del html como este:
<div ng-repeat="item in products" ng-style="{''left'': $index*50 + ''px''}" ng-click="add-to-purchased($index)"></div>
y sobre los productos comprados: en lugar de usar ng-repeat en el conjunto "comprado", dentro de la función "agregar para comprar", después de empujar el producto al conjunto "comprado", simplemente puede animar el producto a la "parte superior" : ''la distancia de altura al elemento bordeado'' "y" izquierda "es igual a {$ scope.purchased.length * 50 + ''px''}. a continuación, agregue una clase usando ng-class (con un toggle) para colorear y otras cosas de CSS ... (también puede considerar la transición para cambios de color, como probablemente sepa)
También creo que puede manejar diferentes alturas y problemas (en caso de que la cantidad de productos supere la capacidad de una línea) con una clase ng que agrega clases con nuevos valores "superiores" basados en: ($ index> some- número), y otra clase ng para el elemento superior (el elemento que está en la parte superior del elemento bordeado), cambiando su altura ...
espero que esto haya sido útil
Actualizar:
desafortunadamente no había entendido bien la pregunta. pero mirando el problema ahora, creo que hay una manera de hacerlo de forma más dinámica.
dentro de la función $scope.purchase
, puede $scope.purchase
su directiva con $broadcast
y pasar el elemento $scope.purchase
esta manera (para cualquier elemento en stock, ya sea creado con ng-repeat o no):
<div class="stock" ng-click="purchase($event)"></div>
y:
$scope.purchase = function(event) {
$scope.purchased.push($scope.products.pop());
$scope.$broadcast(''purchaseHappened'', event.target);
};
y dentro de su directiva, ponga al oyente del evento:
scope.$on(''purchaseHappened'', function(event, target) {
//catch target in here, and then use it''s position to animate the new elements...
})
creo que también puedes usar target.getBoundingClientRect()
para obtener la posición del elemento, relativo a la ventana .top
( .top
, .left
, ...) en lugar de la posición de jquery-ui si quieres ...
¿Está más cerca de la solución?