Skip to content

Instantly share code, notes, and snippets.

@TinyExplosions
Created September 1, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TinyExplosions/f09d19187993dc24b263 to your computer and use it in GitHub Desktop.
Save TinyExplosions/f09d19187993dc24b263 to your computer and use it in GitHub Desktop.
define([
'underscore',
'backbone',
'text!templates/AnnotationTemplate.html',
'hammer',
'utils'
], function(_, Backbone, AnnotationTemplate, Hammer, Utils) {
var AnnotationView = Backbone.View.extend({
img: "photo.jpg",
attributes: {
class: "container transition"
},
stack: [],
redoStack: [],
isDrawing: false,
actions: [],
popupText: "",
line: {
width: 8,
color: "#FF0000",
end: "butt"
},
initialize: function(options) {
document.body.addEventListener('touchmove', this.touchMoveListener, false);
// adding a contains method to make life easier
String.prototype.contains = function(it) {
return this.indexOf(it) !== -1;
};
if (options && options.img) {
this.img = options.img;
}
_.bindAll(this, 'render', 'draw', 'redraw', 'popupChange');
this.render();
},
events: {
"tap .closeWindowLink": "closeView",
"tap .saveImageLink": "saveImage",
"tap .clearLink": "resetCanvas",
"tap .undoLink": "undo",
"tap .redoLink": "redo",
"tap .textLink": "showAttachment",
"tap .highlightLink": "highlightLink",
"tap .lineLink": "setLine",
"touchstart canvas": "draw",
"touchmove canvas": "draw",
"touchend canvas": "draw"
},
render: function() {
var compiledTemplate = _.template(AnnotationTemplate, {});
this.$el.html(compiledTemplate);
$('body').append(this.$el);
setTimeout(function() {
this.$el.addClass('open');
this.initCanvas();
}.bind(this));
this.$el.hammer();
return this;
},
initCanvas: function(evt) {
// get the canvas element and its context
this.stack = [];
this.canvas = this.$el.find('canvas')[0];
this.context = this.canvas.getContext('2d');
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight - 89;
// Draw image on the canvas that was passed in
this.baseImg = new Image();
this.baseImg.src = this.img;
this.baseImg.onload = function() {
this.context.drawImage(this.baseImg, 0, 0, this.canvas.width, this.canvas.height);
}.bind(this);
this.headerHeight = this.$el.find(".heading").height();
this.setLine();
},
closeView: function(evt) {
evt.preventDefault();
this.$el.removeClass('open');
setTimeout(function() {
this.clearListeners();
this.$el.remove();
}.bind(this), 550);
},
touchMoveListener: function(evt) {
evt.preventDefault();
},
clearListeners: function() {
document.body.removeEventListener('touchmove', this.touchMoveListener, false);
},
touchstart: function(coors) {
this.redoStack = [];
this.actions = [];
this.context.beginPath();
this.context.moveTo(coors.x, coors.y);
this.isDrawing = true;
this.actions.push({
context: this.context,
coors: coors,
strokeStyle: this.context.strokeStyle,
lineWidth: this.context.lineWidth,
lineCap: this.context.lineCap,
type: "begin"
});
},
touchmove: function(coors) {
if (this.isDrawing) {
this.context.lineTo(coors.x, coors.y);
this.context.stroke();
this.actions.push({
context: this.context,
coors: coors,
strokeStyle: this.context.strokeStyle,
lineWidth: this.context.lineWidth,
lineCap: this.context.lineCap,
type: "draw"
});
}
},
touchend: function(coors) {
if (this.isDrawing) {
this.touchmove(coors);
this.stack.push(this.actions);
this.actions = [];
this.isDrawing = false;
}
},
draw: function(evt) {
var coors = {
x: this.getX(evt),
y: this.getY(evt)
};
// pass the coordinates to the appropriate handler
this[event.type](coors);
},
getX: function(event) {
if (event.type.contains("touchend")) {
return;
} else if (event.type.contains("touch")) {
var e = event.originalEvent;
return e.targetTouches[0].pageX;
} else {
return event.layerX;
}
},
getY: function(event) {
if (event.type.contains("touchend")) {
return;
} else if (event.type.contains("touch")) {
var e = event.originalEvent;
return e.targetTouches[0].pageY - this.headerHeight;
} else {
return event.layerY;
}
},
split_lines: function(text) {
var mw = this.canvas.width - 10;
this.context.font="12px Verdana";
var words = text.split(' ');
var new_line = words[0];
var lines = [];
for (var i = 1; i < words.length; ++i) {
if (this.context.measureText(new_line + " " + words[i]).width < mw) {
new_line += " " + words[i];
} else {
lines.push(new_line);
new_line = words[i];
}
}
lines.push(new_line);
return lines;
},
saveImage: function(evt) {
evt.preventDefault();
if (this.popupText) {
var lines = this.split_lines(this.popupText);
var offset = (18*lines.length)+20;
var height = this.canvas.height;
this.redraw(height + offset);
this.context.fillStyle = "#333333";
this.context.font="12px Verdana";
for (var i = 0; i < lines.length; i++) {
console.log(height + (15*i)+20);
this.context.fillText(lines[i], 5, height + (18*i)+20);
}
}
var image = this.canvas.toDataURL("image/jpeg");
App.vent.trigger('annotate:complete', {
src: image,
text: this.popupText
});
this.closeView(evt);
},
resetCanvas: function() {
this.stack = [];
this.redoStack = [];
this.popupText = "";
this.popupChange();
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.drawImage(this.baseImg, 0, 0, this.canvas.width, this.canvas.height);
},
undo: function() {
this.hideAttachment();
if (this.stack.length > 0) {
this.redoStack.push(this.stack.pop());
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.redraw();
}
},
redo: function() {
this.hideAttachment();
if (this.redoStack.length > 0) {
this.stack.push(this.redoStack.pop());
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.redraw();
}
},
redraw: function(height, width) {
height = height || this.canvas.height;
width = width || this.canvas.width;
var imgHeight = this.canvas.height;
var imgWidth = this.canvas.width;
this.canvas.height = height;
this.canvas.width = width;
this.context.fillStyle = "#FFFFFF";
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.context.drawImage(this.baseImg, 0, 0, imgWidth, imgHeight);
for (var i = 0; i < this.stack.length; i++) {
for (var j = 0; j < this.stack[i].length; j++) {
var command = this.stack[i][j];
this.context.strokeStyle = command.strokeStyle;
this.context.lineWidth = command.lineWidth;
this.context.lineCap = command.lineCap;
if (command.type === "begin") {
this.context.beginPath();
this.context.moveTo(command.coors.x, command.coors.y);
} else if (command.type === "draw") {
this.context.lineTo(command.coors.x, command.coors.y);
this.context.stroke();
}
}
}
},
showAttachment: function(evt) {
this.$el.find('a').removeClass('selected');
var $popup = this.$el.find('.popup');
var $ul = $(evt.target).closest('ul');
if ($popup.is(':visible')) {
this.hideAttachment();
this.setLine();
return false;
}
this.$el.find('.textLink').addClass('selected');
var bottom = $(window).innerHeight() - ($ul.offset().top - 13);
$popup.css('bottom', bottom);
setTimeout(function() {
$popup.fadeIn('fast').focus();
$popup.on('keyup', this.popupChange);
}.bind(this));
},
popupChange: function(evt) {
if (!evt) {
this.$el.find('.textLink').removeClass("note");
return;
}
this.popupText = this.$el.find('textarea').val();
if (this.popupText) {
this.$el.find('.textLink').addClass("note");
} else {
this.$el.find('.textLink').removeClass("note");
}
},
hideAttachment: function() {
this.$el.find('.lineStyle').fadeOut('fast');
var $popup = this.$el.find('.popup');
$popup.blur().fadeOut('fast', function() {
// $popup.off('keyup', this.popupChange);
}.bind(this));
return false;
},
highlightLink: function() {
this.hideAttachment();
this.$el.find('a').removeClass('selected');
this.$el.find('.highlightLink').addClass('selected');
this.context.strokeStyle = "rgba(243, 243, 21, 0.1)";
this.context.lineWidth = 15;
this.context.lineCap = "round";
},
setLine: function(evt) {
if (evt) {
if ($(evt.target).hasClass('selected')) {
this.$el.find('.lineStyle').fadeIn();
return;
}
}
this.hideAttachment();
this.$el.find('a').removeClass('selected');
this.$el.find('.lineLink').addClass('selected');
this.context.strokeStyle = this.line.color;
this.context.lineWidth = this.line.width;
this.context.lineCap = this.line.end;
}
});
return AnnotationView;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment