What is the easiest way to delay loading of images in Angular - angularjs

I have an app that allows paginating through a large text dataset via the keyboard's right/left arrow keys. The data that is displayed, also contains images.
I would like to delay the loading of these images for 1-2 seconds while the user is paginating through the data quickly.
As soon as the user stops at a certain page, all images should be "lazily" loaded.
I tried to adapt this fiddle to my app without success (see below). http://jsfiddle.net/boneskull/UfrhH/3/
my controller code
$scope.delay_images = function() {
$timeout(function() {
return true;
}, 2000);
};
view code
<tbody>
<tr>
<td style='text-align:left;padding-right:40px; height:250px;width:{{100 / panel.size}}%' ng-repeat="event in data| slice:panel.offset:panel.offset+panel.size">
<h4>{{event._source.Price}} {{event._source.Currency}}</h4>
<img ng-show="delay_images" ng-animate="{show: 'show'}" style='height:100px' ng-src="/img/{{event._source.image_paths}}"/><br /><h6>{{event._source.Title}}</h6>
<button ng-click="build_search(event._source)" ng-show='event._source.ClusterID' type="button" class="btn btn-primary">More like this</button>
</td>
</tr>
</tbody>
I am fairly new to angular.js so any tips that can guide me into the right direction are greatly appreciated.
Thanks!
UPDATE
This is a partially working code based on the accepted answer. BTW, it also implements a delay for the keydownevent.
Right now the code uses ng-show to hide and show images. I realized that this approach is problematic because it triggers a lot of image resource requests in the background while the user is paging.
So the best would be to combine the current solution somehow with the second solution replacing the src= attribute.
How would I go about that? I am catching the keydownevent from a surrounding div, so I have no direct handle to the images. Is there a way to access a class of elements with angular js? Just like with a jquery selector?
var lastFire = 0;
$scope.changeIndex = function($event) {
if($event.keyCode == 37 || $event.keyCode == 39){
var cFire = new Date();
// cancel old timeout
if ($scope.panel.timeoutId) {
$timeout.cancel($scope.panel.timeoutId);
}
// $event.keyCode...
if ((cFire - lastFire) / 1000 > 0.3){
// hide images to start with
$scope.panel.imagesVisible = false;
if ($event.keyCode == 37 && $scope.panel.offset > 0){
$scope.panel.offset = $scope.panel.offset - 10;
//$scope.get_data();
lastFire = cFire;
$scope.panel.timeoutId = $timeout(function() {
$scope.panel.imagesVisible = true;
$scope.panel.timeoutId = undefined;
}, 1000);
}
if ($event.keyCode == 39 && $scope.panel.offset < $scope.data.length - 10){
$scope.panel.offset = $scope.panel.offset + 10;
//$scope.get_data();
lastFire = cFire;
$scope.panel.timeoutId = $timeout(function() {
$scope.panel.imagesVisible = true;
$scope.panel.timeoutId = undefined;
}, 1000);
}
}
else{
$scope.panel.timeoutId = $timeout(function() {
$scope.panel.imagesVisible = true;
$scope.panel.timeoutId = undefined;
}, 1000);
}
}
};

I'd make a custom directive. This is off the top of my head, untested, but something like this should work:
myApp.directive('ngSlowSrc', function($timeout){
return{
restrict: 'A',
link: function(scope, element, attrs){
$timeout(function() {
element.src = attrs.ngSlowSrc;
}, 2000);
}
}
});
now just make your image calls like this: <img ng-slow-src="myimage.jpg" >
The way this works is it just waits 2 seconds to set the src of your image.

I think the simplest thing would be to handle it when you are paging. Let's say this is the function you call to page:
$scope.changeOffset = function(offset) {
$scope.panel.offset = offset;
// cancel old timeout
if ($scope.panel.timeoutId) {
$timeout.cancel($scope.panel.timeoutId);
}
// hide images to start with
$scope.panel.imagesVisible = false;
$scope.panel.timeoutId = $timeout(function() {
$scope.panel.imagesVisible = true;
$scope.panel.timeoutId = undefined;
}, 2000);
});
Then all images on the page can use the flag:
<img ng-show="panel.imagesVisible" ...

Working solution...
used the element method from angular which wraps jquery.
Also included the apply method in between the timeouts to make the ui experience smoother.
Thanks for your input guys.
var lastFire = 0;
$scope.changeIndex = function($event) {
if($event.keyCode == 37 || $event.keyCode == 39){
var cFire = new Date();
// cancel old timeout
if ($scope.panel.timeoutId) {
$timeout.cancel($scope.panel.timeoutId);
}
// $event.keyCode...
if ((cFire - lastFire) / 1000 > 0.5){
if ($event.keyCode == 37 && $scope.panel.offset > 0){
$scope.panel.offset = $scope.panel.offset - 10;
//$scope.get_data();
lastFire = cFire;
$scope.panel.timeoutId = $timeout(function() {
$scope.$apply(function () {
angular.forEach(angular.element('.productImage'), function(value, key){
var elem = angular.element(value);
elem.attr('src',elem.attr('ng-slow-src'));
});
$scope.panel.timeoutId = undefined;
});
}, 1000);
}
if ($event.keyCode == 39 && $scope.panel.offset < $scope.data.length - 10){
$scope.panel.offset = $scope.panel.offset + 10;
//$scope.get_data();
lastFire = cFire;
$scope.panel.timeoutId = $timeout(function() {
$scope.$apply(function () {
angular.forEach(angular.element('.productImage'), function(value, key){
var elem = angular.element(value);
elem.attr('src',elem.attr('ng-slow-src'));
});
$scope.panel.timeoutId = undefined;
});
}, 1000);
}
}
else{
$scope.panel.timeoutId = $timeout(function() {
$scope.$apply(function () {
angular.forEach(angular.element('.productImage'), function(value, key){
var elem = angular.element(value);
elem.attr('src',elem.attr('ng-slow-src'));
});
$scope.panel.timeoutId = undefined;
});
}, 1000);
}
}
};

Related

passing variable value from controller to view in AngularJS

I am developing an application where i popup a window and when it gets popup the next time i will try to open it should not be opened. Next time it should open When i will close the popup then it should open.
Now for that i am using count variable and whenever it will be 1 the popup opens and whenever it is greater than or equal to 2 it shows alert. But now when i close the popup it is not resetting the value of count to 0 in view.
In JSfiddle i tried using var self = this; it works fine but when i tried it on my code it says Uncaught Exception Typeerror cannot find property 'count' defined at line self.count = 0;
How to achieve this? or any alternate solution for this?
<a ui-sref-active="active" ng-click="count=count+1; connectMachine(machine, count)" ng-init="count=0" ><span><i class="fa fa-desktop fa-5x"></i></span></a>
$scope.connectMachine = function(machine, count) {
var promise = restAPIService.connectMachineService(
$scope.thisStudentThisBatch.guacProfileId,
machine.connectionId, $stateParams.batchID).get();
promise.$promise.then(function(response) {
var json = JSON.parse(response.data);
console.log(json.id);
var dnsUrl = $location.absUrl().split('/');
dnsUrl = dnsUrl[0] + '//' + dnsUrl[2];
var apiUrl = dnsUrl + $rootScope.apiUrl + "guacamole/disconnect/"
+ json.id;
var conn_params = $http.defaults.headers.common.Authorization
+ "++" + apiUrl;
$scope.machineURL = response.headers.url + "&conn_params="
+ conn_params;
var params = "height=" + screen.availHeight + ",width="
+ screen.availWidth;
var NewWin;
var self = this;
if ($scope.count == 1) {
NewWin = window.open($scope.machineURL);
} else if ($scope.count >= 2) {
alert("Back Off Back Off");
}
function checkWindow() {
if (NewWin && NewWin.closed) {
window.clearInterval(intervalID);
self.count = 0;
}
}
var intervalID = window.setInterval(checkWindow, 500);
}, function(error) {
dialogs.error("Error", error.data.error, {
'size' : 'sm'
});
});
}
You can use a variable defined on $scope to trigger the value of button instead of using ng-init
HTML:
<div ng-app="myApp">
<ul ng-controller="TodoCtrl">
<li class="list-group-item" ng-repeat="todo in todos">{{todo.text}}
<button class="btn btn-default" ng-click="addLike($index)">value- {{count[$index]}}</button>
</li>
</ul>
</div>
JS:
var myApp = angular.module('myApp', []);
function TodoCtrl($scope) {
$scope.todos = [{
text: 'todo one'
}, {
text: 'todo two',
done: false
}];
$scope.count = [0, 0];
$scope.addLike = function(index) {
var NewWin;
$scope.count[index] ++;
if ($scope.count[index] == 1) {
NewWin = window.open('https://www.google.com');
} else if ($scope.count >= 2) {
alert("Back Off Back Off");
}
function checkWindow() {
if (NewWin && NewWin.closed) {
window.clearInterval(intervalID);
$scope.count[index] = 0;
$scope.$apply();
}
}
var intervalID = window.setInterval(checkWindow, 500);
};
};
JS Fiddle :https://jsfiddle.net/p41dLjmn/

Controller function not applying to ng-include file

I am trying to use the "minisCtrlOverall" controller in my ng-include file, but none of the functionality in my controller works for the included file unless I put ng-controller in the actual overall.html file. What I am trying to do is access the "ring-fill" class that is in my overall.html file from my controller. I want to add the class "active" too all "ring-fills" but its not working. I'm guessing this isn't working because the included files comes after the controller runs? Anyone know how I can fix this?
Controller:
angular.module('ciscoImaDashboardAdmin',[])
.controller('minisCtrlOverall', function ($scope, $rootScope, dummyData) {
$scope.overallOn = true;
$scope.switchView = function(element) {
var value = element.target.attributes['value'].value;
if(value == "overall") {
$scope.overallOn = true;
$scope.swimlaneOn = false;
}
else if(value == "swimlane") {
$scope.swimlaneOn = true;
$scope.overallOn = false;
}
};
var totalRings = 9;
var maxScore = 10;
var data_overall = {
average_score: 6
}
var ringToHighlight = Math.floor((data_overall.average_score/maxScore)*totalRings); //round down
var i = 0;
var rings = [];
var ringClass = 'path.ring-fill:not(.ring-border)';
$(ringClass).each(function(){
rings.push($(this)[0]);
});
while( i < ringToHighlight) {
fillPath(i);
i = i + 1;
}
function fillPath(i) {
if(i < ringToHighlight) {
var selectedRing = $(rings[i]);
selectedRing.attr("class", "ring-fill active");
}
}
});
HTML:
<div class="row mini" ng-show="overallOn" ng-controller="minisCtrlOverall">
<div class="col-sm-3">
<div ng-include="'svgs/overall.html'"></div>
</div>
</div>

Angular ui-bootstrap typeahead suggestion - scroll

According to this link :
up/down arrow key issue with typeahead control (angular bootstrap UI)
i have added these line in my js:
.directive('shouldFocus', function(){
return {
restrict: 'A',
link: function(scope,element,attrs){
scope.$watch(attrs.shouldFocus,function(newVal,oldVal){
element[0].scrollIntoView(false);
});
}
};
})
while scrolling, we get some more disturb, scroll was not smoothy.
Before adding this code, scroll was normal and smoothy.
Please anyone help me?
Hi here is another code only for keyup event to adjust the scroll height while up/down keypress
you need to add a function only to keyup in the typeahead directive in angular ui
search directive('typeahead' in the angular ui js file
find fireRecalculating function before it paste the following function
function makeSureVisible(){
$timeout(function(){
$el = popUpEl.find('.active');
if($el.length>0)
{
var elTop, elBottom, nodeScrollTop, nodeHeight;
elTop = $el.position().top;
console.log(elTop);
elBottom = elTop + $el.outerHeight(true);
nodeScrollTop = popUpEl.scrollTop();
nodeHeight = popUpEl.height() + parseInt(popUpEl.css("paddingTop"), 10) + parseInt(popUpEl.css("paddingBottom"), 10);
if (elTop < 0) {
popUpEl.scrollTop(nodeScrollTop + elTop);
} else if (nodeHeight < elBottom) {
popUpEl.scrollTop(nodeScrollTop + (elBottom - nodeHeight));
}
}
});
}
now find keyup function attached. call the above function
element.bind('keydown', function(evt) {
//typeahead is open and an "interesting" key was pressed
if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
return;
}
// if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
resetMatches();
scope.$digest();
return;
}
evt.preventDefault();
if (evt.which === 40) {
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
scope.$digest();
makeSureVisible();
} else if (evt.which === 38) {
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
scope.$digest();
makeSureVisible();
} else if (evt.which === 13 || evt.which === 9) {
scope.$apply(function () {
scope.select(scope.activeIdx);
});
} else if (evt.which === 27) {
evt.stopPropagation();
resetMatches();
scope.$digest();
}
});
function makeSureVisible() {
$timeout(function () {
$el = popUpEl[0].querySelector('.active');
if ($el) {
var elTop, elBottom, nodeScrollTop, nodeHeight;
elTop = $el.offsetTop;
elBottom = elTop + $el.offsetHeight;
nodeScrollTop = popUpEl[0].scrollTop;
nodeHeight = popUpEl[0].offsetHeight - (parseInt(window.getComputedStyle(popUpEl[0], null).getPropertyValue('padding-top')) + parseInt(window.getComputedStyle(popUpEl[0], null).getPropertyValue('padding-bottom')));
if (elTop < nodeScrollTop) {
popUpEl[0].scrollTop = elTop;
} else if (nodeHeight < elBottom) {
popUpEl[0].scrollTop = nodeScrollTop + elBottom - nodeHeight;
}
}
});
}
I bumped into the same issue with latest version of angular bootstrap 0.14.3 as of 27/11/2015.
Comments:
I placed the function in the element.bind("keydown") since it didn't work where you suggested. (function wasn't in proper scope and was undefined)
The first "if" wasn't triggered for me so i changed the logic a bit. When user reaches the end the dropdown scrolls to top properly in my case.
Modified it to work for plain javascript.
Thank you for your solution!
I had previously resolved this issue using the "shouldFocus" directive but I had to make more tweaks to get this to work. Perhaps this version will work for you.
.directive('shouldFocus', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(attrs.shouldFocus, function (newVal, oldVal) {
if (newVal && element.prop("class").indexOf("active")) {
var par = element.parent("ul");
var cellHeight = element.children().innerHeight();
var maxHeight = par.height();
var startIndex = Math.floor(maxHeight / cellHeight);
if (scope.$index > startIndex) {
var scroll = (scope.$index - startIndex) * cellHeight;
par.scrollTop(scroll);
}
if (scope.$index === 0) {
par.scrollTop(0);
}
}
});
}
}
})
Here is the modified template for those who don't know where to add the directive:
angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/typeahead/typeahead-popup.html",
"<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
" <li ng-repeat=\"match in matches track by $index\" should-focus=\"isActive($index)\" ng-class=\"{active: isActive($index) }\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{::match.id}}\">\n" +
" <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
" </li>\n" +
"</ul>\n" +
"");
}]);

ui-select multiselect is very slow in displaying the choices

I ran into this problem, and I don't know how to solve it. I have used a ui-select multiselect in my page. First, a http.get request is made to a url which gets the data, then the ui-select choices are populated. The data is big - the length of the data is 2100. This data is to be shown as choices. (The data is fetched at the beginning during the loading of the page and is stored in an array)
But the problem is each time I click on the multiselect to select a choice, it takes 4-5 seconds to populate the list and the page becomes very slow. What do I do to reduce this time?
The choices data is stored in an array, the datatype is array of strings.
<ui-select multiple ng-model="selectedFields.name" style="width: 100%;">
<ui-select-match placeholder="Select fields...">{{$item}}</ui-select-match>
<ui-select-choices repeat="fields in availableFields | filter:$select.search">
{{fields}}
</ui-select-choices>
</ui-select>
in the controller,
$scope.selectedFields = {};
$scope.selectedFields.name = [];
$scope.init = function() {
$http.get(url)
.success( function(response, status, headers, config) {
availableFields = response;
})
.error( function(err) {
});
};
$scope.init();
If not this way, is there any other options/choice I can work with which doesn't delay showing the select-choices?
This is a known issue in ui-select. I tried the following ways, both work
1) There is a workaround for this - use
| limitTo: 100
This limits the choice display to 100 but all the choices can be selected. Look at this thread for more details.
2) Since some of the time, there is a need to display the entire list in the choices, 1) is not a viable option. I used a different library - selectize.js. Here's a plunker demo given in the page
Here is complete solution that decorates uiSelectChoices directive.
Items are populated progressively as the user scrolls.
Also takes care of searches in the scrolls.
Also works for all values of position={auto, up, down}
Example
<ui-select-choices
position="up"
all-choices="ctrl.allTenThousandItems"
refresh-delay="0"
repeat="person in $select.pageOptions.people | propsFilter: {name: $select.search, age: $select.search} ">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
Working Plnkr
Also with With v0.19.5
The directive
app.directive('uiSelectChoices', ['$timeout', '$parse', '$compile', '$document', '$filter', function($timeout, $parse, $compile, $document, $filter) {
return function(scope, elm, attr) {
var raw = elm[0];
var scrollCompleted = true;
if (!attr.allChoices) {
throw new Error('ief:ui-select: Attribute all-choices is required in ui-select-choices so that we can handle pagination.');
}
scope.pagingOptions = {
allOptions: scope.$eval(attr.allChoices)
};
attr.refresh = 'addMoreItems()';
var refreshCallBack = $parse(attr.refresh);
elm.bind('scroll', function(event) {
var remainingHeight = raw.offsetHeight - raw.scrollHeight;
var scrollTop = raw.scrollTop;
var percent = Math.abs((scrollTop / remainingHeight) * 100);
if (percent >= 80) {
if (scrollCompleted) {
scrollCompleted = false;
event.preventDefault();
event.stopPropagation();
var callback = function() {
scope.addingMore = true;
refreshCallBack(scope, {
$event: event
});
scrollCompleted = true;
};
$timeout(callback, 100);
}
}
});
var closeDestroyer = scope.$on('uis:close', function() {
var pagingOptions = scope.$select.pagingOptions || {};
pagingOptions.filteredItems = undefined;
pagingOptions.page = 0;
});
scope.addMoreItems = function(doneCalBack) {
console.log('new addMoreItems');
var $select = scope.$select;
var allItems = scope.pagingOptions.allOptions;
var moreItems = [];
var itemsThreshold = 100;
var search = $select.search;
var pagingOptions = $select.pagingOptions = $select.pagingOptions || {
page: 0,
pageSize: 20,
items: $select.items
};
if (pagingOptions.page === 0) {
pagingOptions.items.length = 0;
}
if (!pagingOptions.originalAllItems) {
pagingOptions.originalAllItems = scope.pagingOptions.allOptions;
}
console.log('search term=' + search);
console.log('prev search term=' + pagingOptions.prevSearch);
var searchDidNotChange = search && pagingOptions.prevSearch && search == pagingOptions.prevSearch;
console.log('isSearchChanged=' + searchDidNotChange);
if (pagingOptions.filteredItems && searchDidNotChange) {
allItems = pagingOptions.filteredItems;
}
pagingOptions.prevSearch = search;
if (search && search.length > 0 && pagingOptions.items.length < allItems.length && !searchDidNotChange) {
//search
if (!pagingOptions.filteredItems) {
//console.log('previous ' + pagingOptions.filteredItems);
}
pagingOptions.filteredItems = undefined;
moreItems = $filter('filter')(pagingOptions.originalAllItems, search);
//if filtered items are too many scrolling should occur for filtered items
if (moreItems.length > itemsThreshold) {
if (!pagingOptions.filteredItems) {
pagingOptions.page = 0;
pagingOptions.items.length = 0;
} else {
}
pagingOptions.page = 0;
pagingOptions.items.length = 0;
allItems = pagingOptions.filteredItems = moreItems;
} else {
allItems = moreItems;
pagingOptions.items.length = 0;
pagingOptions.filteredItems = undefined;
}
} else {
console.log('plain paging');
}
pagingOptions.page++;
if (pagingOptions.page * pagingOptions.pageSize < allItems.length) {
moreItems = allItems.slice(pagingOptions.items.length, pagingOptions.page * pagingOptions.pageSize);
}
for (var k = 0; k < moreItems.length; k++) {
pagingOptions.items.push(moreItems[k]);
}
scope.calculateDropdownPos();
scope.$broadcast('uis:refresh');
if (doneCalBack) doneCalBack();
};
scope.$on('$destroy', function() {
elm.off('scroll');
closeDestroyer();
});
};
}]);
As stated, ui-select is having quite a few performance issues, but there is a workaround for the limit issue.
If you follow akashrajkn's approach then you will notice that it will actually cut out important pieces of data because it will only render 100 at a time. There is a fix that has passed the unit tests and it can be found on the thread here:
https://github.com/angular-ui/ui-select/pull/716
Basically, if you are storing the javascript file locally, then you can adjust the unminified version. All you need to do is implement the changes he made in the pull request and it should help out significantly. In order to apply the limiting factor, take a look at the below, modified example:
<ui-select multiple ng-model="selectedFields.name" limit = "10" style="width: 100%;">
<ui-select-match placeholder="Select fields...">{{$item}}</ui-select-match>
<ui-select-choices repeat="fields in availableFields | filter:$select.search | limitTo:$select.limit ">
{{fields}}
</ui-select-choices>
</ui-select>
The above will limit your data in the drop down while also maintaining the level of consistency needed.
Because I cannot leave a comment (not enough rep) I write this as an answer and I am sorry it is no answer for the problem.
#bhantol I changed the following line of code to your solution which is working perfectly for me so far
for (var k = 0; k < moreItems.length; k++) {
pagingOptions.items.push(moreItems[k]);
}
for (var k = 0; k < moreItems.length; k++) {
if (pagingOptions.items.indexOf(moreItems[k]) == -1){
pagingOptions.items.push(moreItems[k]);
}
}
This prevents duplicated items from showing up if the user is starting to write a filter and then deletes it.
Also I just figured out that if the list is smaller than 20 items it will not work so I changed:
if (pagingOptions.page * pagingOptions.pageSize < allItems.length) {
moreItems = allItems.slice(pagingOptions.items.length, pagingOptions.page * pagingOptions.pageSize);
}
to:
if (pagingOptions.page * pagingOptions.pageSize < allItems.length) {
moreItems = allItems.slice(pagingOptions.items.length, pagingOptions.page * pagingOptions.pageSize);
}
else{ moreItems = allItems;}
Maybe this will help you somehow and sorry again for not answering the question.

Why is my custom signature pad directive not showing canvas? (without jQuery)

Hi I'm working on a signature pad for angularjs without importing jquery, i'm just using elements of jquery lite that angular uses. I have hit a wall and I have no idea how to get past it. Here's my code:
var sig = angular.module('signature', []);
sig.directive("signatureDirective", function () {
return {
template: '<canvas id="canvas" width="500" height="100" style="border: 1px solid #ccc;"></canvas>',
restrict: 'E',
link: function (scope, element, attrs) {
var canvas = $(element);
var context = canvas.getContext("2d");
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
canvas.on("mousedown", mouseDown, false);
canvas.on("mousemove", mouseXY, false);
document.body.on("mouseup", mouseUp, false);
canvas.on("touchstart", mouseDown, false);
canvas.on("touchmove", mouseXY, true);
canvas.on("touchend", mouseUp, false);
canvas.on('touchmove', function (e) {
pen.x = e.pageX;
pen.y = e.pageY;
});
canvas.on('touchstart', function (e) {
context.fillRect(pen.x, pen.y, 1, 1);
});
canvas.on('mousemove', function (e) {
pen.x = e.pageX;
pen.y = e.pageY;
});
canvas.on('mousedown', function (e) {
context.fillRect(pen.x, pen.y, 1, 1);
});
context.getImageData();
document.body.on("touchcancel", mouseUp, false);
function draw() {
context.clearRect(0, 0, 500, 100);
context.strokeStyle = "#000000";
context.lineJoin = "miter";
context.lineWidth = 2;
for (var i = 0; i < clickX.length; i++) {
context.beginPath();
if (clickDrag[i] && i) {
context.moveTo(clickX[i - 1], clickY[i - 1]);
} else {
context.moveTo(clickX[i] - 1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.stroke();
context.closePath();
}
}
function mouseDown(e) {
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
paint = true;
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
draw();
}
function addClick(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function mouseUp() {
paint = false;
}
function mouseXY(e) {
if (paint) {
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
draw();
}
}
}
};
});
I append it as an element but it doesnt show and javascript doesn't throw any exceptions. Can anyone help?
I found a great answer for you on stackoverflow, a canvas drawing directive with live example.
example: http://plnkr.co/aG4paH
HTML:
<div class="col-md-8" cg-signature>
<div class="sig sigWrapper">
<div class="typed"></div>
<canvas class="pad" width="197" height="52"></canvas>
<input type="hidden" name="output" class="output">
<a class="clearButton" href="#">Clear</a>
</div>
</div>
JavaScript:
angular.module('YourModuleName')
.directive('directiveName', function() {
var directive = {};
directive.restrict = 'A';
directive.link = function($scope, $ele, $attrs){
$ele.addClass('sigPad')
$ele.ready(function() {
$ele.signaturePad({drawOnly:true});
});
};
return directive;
});

Resources