AngularJS scope.watch 以非常慢的速度触发并且错过了更改
Posted
技术标签:
【中文标题】AngularJS scope.watch 以非常慢的速度触发并且错过了更改【英文标题】:AngularJS scope.watch fired at very slow rate and misses changes 【发布时间】:2016-02-19 21:46:27 【问题描述】:我正在尝试将一个变量从控制我的侧边菜单抽屉(基于https://github.com/driftyco/ionic-ion-drawer)的控制器传递给内容指令,以便能够根据侧边菜单的打开程度改变内容的不透明度.
我的第一次尝试是使用 $scope.$watch 抽屉.style.transport.translate3d 来做到这一点,使用类似: How do I get the absolute value of translate3d? 但没有成功,所以我改变了获取抽屉控制器使用的当前变量的方式。
现在的问题是,查看控制台手表只会从值 outX 的 20 或 30 次更改中触发一次。我已经看到在 codepen 中最糟糕的是 $watch 永远不会被触发,但是在我的浏览器中直接从 IntelliJ 或我的 android 设备加载时,我在日志中看到了这一点:
Starting drag
c_menu.js:166 Offset: 16
c_menu.js:227 48 16 32 -418
c_menu.js:227 49 16 33 -417
c_menu.js:227 50 16 34 -416
c_menu.js:227 51 16 35 -415
c_menu.js:227 52 16 36 -414
c_menu.js:227 54 16 38 -412
c_menu.js:227 55 16 39 -411
c_menu.js:227 56 16 40 -410
c_menu.js:227 57 16 41 -409
c_menu.js:227 58 16 42 -408
c_menu.js:227 59 16 43 -407
c_menu.js:227 61 16 45 -405
c_menu.js:227 62 16 46 -404
c_menu.js:227 63 16 47 -403
c_menu.js:227 64 16 48 -402
c_menu.js:227 65 16 49 -401
c_menu.js:227 66 16 50 -400
c_menu.js:227 68 16 52 -398
d_menu.js:62 -398
c_menu.js:227 67 16 51 -399
c_menu.js:227 68 16 52 -398
c_menu.js:227 69 16 53 -397
c_menu.js:227 70 16 54 -396
c_menu.js:227 71 16 55 -395
c_menu.js:227 72 16 56 -394
d_menu.js:62 -394
c_menu.js:227 73 16 57 -393
c_menu.js:227 75 16 59 -391
c_menu.js:227 76 16 60 -390
c_menu.js:227 77 16 61 -389
c_menu.js:227 78 16 62 -388
c_menu.js:227 79 16 63 -387
c_menu.js:227 80 16 64 -386
d_menu.js:62 -386
c_menu.js:227 82 16 66 -384
c_menu.js:227 83 16 67 -383
c_menu.js:227 84 16 68 -382
c_menu.js:227 85 16 69 -381
c_menu.js:227 86 16 70 -380
c_menu.js:227 87 16 71 -379
d_menu.js:62 -379
c_menu.js:227 87 16 71 -379
c_menu.js:227 86 16 70 -380
c_menu.js:227 85 16 69 -381
c_menu.js:227 84 16 68 -382
c_menu.js:227 83 16 67 -383
c_menu.js:227 81 16 65 -385
c_menu.js:227 80 16 64 -386
c_menu.js:227 79 16 63 -387
c_menu.js:227 78 16 62 -388
c_menu.js:227 77 16 61 -389
d_menu.js:62 -389
c_menu.js:227 76 16 60 -390
c_menu.js:227 74 16 58 -392
c_menu.js:227 75 16 59 -391
c_menu.js:227 76 16 60 -390
c_menu.js:227 77 16 61 -389
c_menu.js:227 78 16 62 -388
c_menu.js:227 79 16 63 -387
c_menu.js:227 80 16 64 -386
d_menu.js:62 -386
c_menu.js:191 End drag
我正在尝试做的事情的一个小版本可以在这里找到: http://codepen.io/taquionbcn/pen/yYZmVY?editors=101 html
<link href="http://code.ionicframework.com/1.0.0/css/ionic.css" rel="stylesheet">
<script src="http://code.ionicframework.com/1.0.0/js/ionic.bundle.js"></script>
</head>
<body ng-app="myapp" >
<drawer side="left">
<ion-content>
<h2>Menu</h2>
<button ng-click="closeDrawer()">Close</button>
<ion-list>
<ion-item>Friends</ion-item>
<ion-item>Favorites</ion-item>
<ion-item>Search</ion-item>
</ion-list>
</ion-content>
</drawer>
<ion-pane>
<content>
<button ng-click="openDrawer()">Open</button>
</content>
</ion-pane>
</body>
</html>
AngularJS
var app = angular.module('myapp', ['ionic'])
//'use strict';
/**
* The ionic-contrib-frosted-glass is a fun frosted-glass effect
* that can be used in ios apps to give an iOS 7 frosted-glass effect
* to any element.
*/
app.controller(
'drawerCtrl',
['$scope','$element', '$attrs', '$ionicGesture', '$document',
function($scope,$element, $attr, $ionicGesture, $document)
var el = $element[0];
var dragging = false;
var startX, lastX, offsetX,newX;
$scope.outX = -1;
var side;
// How far to drag before triggering
var thresholdX = 15;
// How far from edge before triggering
var edgeX = 40;
var LEFT = 0;
var RIGHT = 1;
var isTargetDrag = false;
var width = $element[0].clientWidth;
var enableAnimation = function()
$element.addClass('animate');
;
var disableAnimation = function()
$element.removeClass('animate');
;
// Check if this is on target or not
var isTarget = function(el)
while (el)
if (el === $element[0])
return true;
el = el.parentNode;
;
var startDrag = function(e)
disableAnimation();
dragging = true;
offsetX = lastX - startX;
console.log('Starting drag');
console.log('Offset:', offsetX);
;
var startTargetDrag = function(e)
disableAnimation();
dragging = true;
isTargetDrag = true;
offsetX = lastX - startX;
console.log('Starting target drag');
console.log('Offset:', offsetX);
;
var doEndDrag = function(e)
startX = null;
lastX = null;
offsetX = null;
isTargetDrag = false;
if (!dragging)
return;
dragging = false;
console.log('End drag');
enableAnimation();
ionic.requestAnimationFrame(function()
if ($scope.newX < (-width / 2))
el.style.transform = el.style.webkitTransform = 'translate3d(' + -width + 'px, 0, 0)';
else
el.style.transform = el.style.webkitTransform = 'translate3d(0px, 0, 0)';
);
;
var doDrag = function(e)
if (e.defaultPrevented)
return;
if (!lastX)
startX = e.gesture.touches[0].pageX;
lastX = e.gesture.touches[0].pageX;
if (!dragging)
// Dragged 15 pixels and finger is by edge
if (Math.abs(lastX - startX) > thresholdX)
if (isTarget(e.target))
startTargetDrag(e);
else if (startX < edgeX)
startDrag(e);
else
//console.log(lastX, offsetX, lastX - offsetX);
newX = Math.min(0, (-width + (lastX - offsetX)));
$scope.outX =newX;
// console.log(lastX, offsetX, lastX - offsetX,newX);
ionic.requestAnimationFrame(function()
el.style.transform = el.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)';
);
if (dragging)
e.gesture.srcEvent.preventDefault();
;
side = $attr.side == 'left' ? LEFT : RIGHT;
console.log(side);
$ionicGesture.on('drag', function(e)
doDrag(e);
, $document);
$ionicGesture.on('dragend', function(e)
doEndDrag(e);
, $document);
this.close = function()
enableAnimation();
ionic.requestAnimationFrame(function()
if (side === LEFT)
el.style.transform = el.style.webkitTransform = 'translate3d(-100%, 0, 0)';
else
el.style.transform = el.style.webkitTransform = 'translate3d(100%, 0, 0)';
);
;
this.open = function()
enableAnimation();
ionic.requestAnimationFrame(function()
if (side === LEFT)
el.style.transform = el.style.webkitTransform = 'translate3d(0%, 0, 0)';
else
el.style.transform = el.style.webkitTransform = 'translate3d(0%, 0, 0)';
);
;
])
app.directive('drawer', ['$rootScope', '$ionicGesture', function($rootScope, $ionicGesture)
return
restrict: 'E',
controller: 'drawerCtrl',
link: function($scope, $element, $attr, ctrl)
$element.addClass($attr.side);
$scope.openDrawer = function()
console.log('open');
ctrl.open();
;
$scope.closeDrawer = function()
console.log('close');
ctrl.close();
;
]);
app.directive('drawerClose', ['$rootScope', function($rootScope)
return
restrict: 'A',
link: function($scope, $element)
$element.bind('click', function()
var drawerCtrl = $element.inheritedData('$drawerController');
drawerCtrl.close();
);
]);
app.directive('content',[ function()
return
restrict: 'E',
//controller: 'contentCtrl',
scope:
outX: '='
,
link: function ($scope, $element, $attr)
console.log($scope);
console.log($element);
$element[0].style.opacity = 0.5;
$scope.$watch('outX',function(nv)
console.log($scope.outX);
//console.log($element);
//console.log($scope);
);
;
]);
CSS
drawer
display: block;
position: fixed;
width: 270px;
height: 100%;
z-index: 100;
background: #ffffff;
drawer.animate
-webkit-transition: 0.4s all ease-in-out;
transition: 0.4s all ease-in-out;
drawer.left
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
box-shadow: 1px 0px 10px rgba(0,0,0,0.3);
drawer.right
right: 0;
top: 0;
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
box-shadow: -1px 0px 10px rgba(0,0,0,0.3);
编辑:
一段时间后,我回到了这个话题,并了解了更多关于离子、角度和所有这些东西的知识,我已经采取了一些方法。
对于黑屏,因为我将有多个侧边菜单,并且我决定在正文中使用与侧边菜单和 ion-nav-view 平行的窗口:
<darkscreen class="darkscreen" id="iddarkscreen"></darkscreen>
及其指令:
menu.directive('darkscreen',function()
return
restrict: 'E',
link : function($scope,$element,$attr)
console.log(arguments)
,
controller: function($scope,$interval,operativeDB)
console.log(arguments);
var iddarkscreen = document.getElementById("iddarkscreen");
$scope.getdarkvalue = function()
//console.log(JSON.stringify(operativeDB.getmov()));
return operativeDB.getdark();
;
$scope.$watch($scope.getdarkvalue,function(newVal)
if(newVal > 0) iddarkscreen.style.visibility = 'visible'; else iddarkscreen.style.visibility = 'hidden';
,true);
);
和css:
.darkscreen
z-index: 50;
background-color: black;
position: absolute;
height: 100%;
width: 100%;
opacity: .7;
transition: opacity 300ms ease-in-out;
下一步是修改 getdarkvalue 函数中的 z-index 参数,使其始终位于目标之下并屏蔽其余部分。 最后一步是使不透明度渐进
【问题讨论】:
嗨 tasseKATT,是的,但我在等待更多答案,所以我开始做我的应用程序的其他事情,我同意你在 doDrag 上调用 $scope.$apply 会计算昂贵,但我'还没试过。一旦我继续这部分,我会说些什么。 完全没问题,谢谢你的更新:) 自上次编辑“2016 年 4 月 6 日”以来还没有,现在正在满足我的需要。它在待办事项清单上,但我还有其他优先任务。它缺少取决于拖动索引的不透明度变量。 这方面有什么更新吗? 没有 tasseKATT,我不再使用 Ionic 或其他网络、应用程序代码。我回到硬件:) 希望你找到最好的解决方案。我能说的是,在我的地方继续工作的人停止做 ionic 并回到原生 android java,我看到的最后一件事比 Ionic App 的行为要好得多。 【参考方案1】:通过$ionicGesture.on
附加的监听器函数在 Angular 的上下文之外执行并且不会触发摘要循环,这意味着监听器不会看到任何变化。
如果您在doDrag
函数中添加$scope.$apply()
,您会注意到观察者将开始看到更改。
虽然我不会推荐这个。由于doDrag
函数执行了很多次,它确实会给应用程序带来同样多次执行摘要循环的负担,尤其是在大型应用程序和移动设备上。
在这种情况下,我建议使用监听拖动事件并直接修改元素不透明度的指令,而不是依赖摘要循环和 Angular 的数据绑定。
【讨论】:
以上是关于AngularJS scope.watch 以非常慢的速度触发并且错过了更改的主要内容,如果未能解决你的问题,请参考以下文章
如何在 AngularJS 中使用 $scope.$watch 和 $scope.$apply?
angularjs中使用$scope.$watch监控对象模型的变化
AngularJS中的$watch(),$digest()和$apply()