Подтвердить что ты не робот

Как оживить элементы ng-repeat относительно событий кликов, вызывающих изменение

Я пытаюсь анимировать пользователя, выбирающего элементы из разных наборов элементов. Элемент должен анимировать от установленного кликом к нему новую позицию в списке выбранных элементов.

В приведенном ниже примере рассмотрите розовые коробки как доступные предметы, а рамку в виде списка выбранных элементов (синие квадратики). Пользователь может выбрать элемент, нажав на любой из розовых коробок:

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>
4b9b3361

Ответ 1

если я правильно понял ваш вопрос (скажите, если нет); я думаю, что один из способов справиться с этой проблемой, выглядит следующим образом:

при условии, что размер (ширина) ваших продуктов будет постоянным - до 50 пикселей или что-то еще; вы можете установить абсолютное положение розовых элементов; затем используйте ng-repeat для розовых элементов с кратким атрибутом ng-стиля внутри html следующим образом:

<div ng-repeat="item in products" ng-style="{'left': $index*50 + 'px'}" ng-click="add-to-purchased($index)"></div>

и о приобретенных продуктах: вместо использования ng-repeat в "приобретенном" массиве, внутри функции "добавление к покупателю", после нажатия продукта на "приобретенный" массив, вы можете просто анимировать продукт до "top:" расстояние высоты до граничного элемента "и" слева" равно {$ scope.purchased.length * 50 + 'px'}. затем добавьте класс, используя ng-класс (с переключателем) для раскраски и другого материала css... (вы также можете рассмотреть переход для изменения цвета, как вы, вероятно, знаете)

Я также считаю, что вы можете справиться с проблемой разных высот и вершин (в случае, если количество продуктов становится более одной линейной емкости) с ng-классом, который добавляет классы с новыми "верхними" значениями на основе: ($ index > some-number) и еще один ng-класс для верхнего элемента (элемент, который находится поверх граничного элемента), изменяя его высоту...

Надеюсь, это было полезно


Update:

К сожалению, я не понял этот вопрос хорошо. но, глядя на проблему сейчас, я думаю, что есть способ сделать это более динамично.

внутри функции $scope.purchase вы можете сообщить свою директиву с помощью $broadcast и передать элемент с щелчком, подобный этому (для любого элемента на складе, созданного с помощью ng-repeat или нет):

<div class="stock" ng-click="purchase($event)"></div>

и

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

и внутри вашей директивы, поставьте прослушиватель событий:

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

Я думаю, вы также можете использовать target.getBoundingClientRect(), чтобы получить позицию элемента относительно окна просмотра (.top, .left,...) вместо jquery-ui .position, если вы хотите...

он ближе к решению?

Ответ 2

Это решение является улучшением в том, что оно устраняет необходимость добавления информации в область действия функции Purchase и позволяет избежать смешивания данных модели и деталей пользовательского интерфейса с помощью директивы "source" и хранения информации о начале в свойстве контроллера. Пример упрощен и, конечно же, может быть улучшен. Ключевым моментом является то, что данные, необходимые для управления процессом, никогда не отображаются через область видимости.

Если целевой элемент подвергается удалению из DOM (т.е. является частью ng-repeat, тогда это решение нужно будет немного изменить, чтобы вычислить и сохранить начальные позиции анимации как часть обработчика monitor.click вместо сохранения самого целевого элемента.

Метод анимации кажется мне произвольным. В этом примере используется метод анимации OP jqueryUI, но он будет работать также с переходами css или с использованием $animate.

Полный пример здесь

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;
      }
    }
  };
});