Capture a image block on angular

最近需要使用到一個圖片切割的工具,在angular的package中找了一下,沒有找到比較合適,因為只需要支援html5的瀏覽器,就實作了一個間單的工具(ngImageEditor)。

以下是demo實際執行的畫面:

explain

使用與安裝:

Demo

demo

Install

1
bower install ngImageEditor

Support

  • IE9+
  • chrome
  • firefox

設定editor的attributes

1
<div img-src="imgSrc" ng-image-editor="imageEditor" selected="selected"></div>

載入要切割的圖片

1
$scope.imgSrc='/images/head.jpeg';

設定切割的區塊位置及大小

1
$scope.selected = {width:150,height:150,top:0,left:0};

將區塊大小的image取出

1
$scope.imageEditor.toDataURL();

實作方式主要是透過canvas作轉換,至於cross domain,必須是同個domain,否則要從server上設定,允許哪個domain可以存取作編輯。

在server上設定你要跨網域的domain:

1
Access-Control-Allow-Origin: http://your.domain.com

在javascript中呼叫img.src之前,加入以下設定:

1
img.crossOrigin = "Anonymous";

可參考這篇

jquery detach on angular directive

在angular中會有基本的jquery-lite,也就是angular.element,當在include angular之前,就先include jquery,此時angular的jquery-lite,就會被自動替換成jquery。

替換angular.element的source code如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function bindJQuery() {
// bind to jQuery if present;
jQuery = window.jQuery;
// reset to jQuery or default to us.
if (jQuery) {
jqLite = jQuery;
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: JQLitePrototype.controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
// Method signature:
// jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
jqLitePatchJQueryRemove('remove', true, true, false);
jqLitePatchJQueryRemove('empty', false, false, false);
jqLitePatchJQueryRemove('html', false, false, true);
} else {
jqLite = JQLite;
}
angular.element = jqLite;
}

另外一個需要注意的地方就是jqLitePatchJQueryRemove,會將jquery原生的function,多經過一層處理,處理完在呼叫jquery的function

jqLitePatchJQueryRemove的source如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {     
var originalJqFn = jQuery.fn[name];
originalJqFn = originalJqFn.$original || originalJqFn;
removePatch.$original = originalJqFn;
jQuery.fn[name] = removePatch;

function removePatch(param) {
// jshint -W040
var list = filterElems && param ? [this.filter(param)] : [this],
fireEvent = dispatchThis,
set, setIndex, setLength,
element, childIndex, childLength, children;

if (!getterIfNoArguments || param != null) {
while(list.length) {
set = list.shift();
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
element = jqLite(set[setIndex]);
if (fireEvent) {
element.triggerHandler('$destroy');
} else {
fireEvent = !fireEvent;
}
for(childIndex = 0, childLength = (children = element.children()).length;
childIndex < childLength;
childIndex++) {
list.push(jQuery(children[childIndex]));
}
}
}
}
return originalJqFn.apply(this, arguments);
}
}

其中element.triggerHandler('$destroy')會告知angular底下的transcludedScope,這個element已經被摧毀了,所以會導致,$element.detach的時候,transcludedScope無法被更新。element.triggerHandler('$destroy')的監聽event可以參考createBoundTranscludeFn這個function,會有下這段code去監聽:

1
clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));

jqLitePatchJQueryRemove只替換removeemptyhtml,而沒有替換detach,使用detach卻會影響scope,真正原因要找jquery的source code。

因為detach間接呼叫了remove:

1
2
3
detach: function( selector ) {
return this.remove( selector, true );
}

Example(1.2.7):

由此可知,只要scope尚未ready時,呼叫detach就不會中斷scope的更新。若要讓directive的scope正常運作,可用以下方式:

  1. 不使用transclude
  2. 不在scope ready中,使用detach
  3. 使用原生api
  4. 最後一種方式,jquery在angular之後include

如果不是這麼需要使用到jquery,建議就不要include了,當然jquery也提供很多方便的功能,依照project需求自己評估吧!