First Evaluation
Files modified in this directory. https://github.com/vchilkuri/vchilkuri.github.io/blob/master/src/ccNetViz.js
let offset = 2.0 * nodeStyle.maxSize; //added offset for screenspace GSoC 17
/* GSoc 17 code for touch events */
this.enableTouch = function(image){
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
// some code only for small devices..
//alert(44);
/*var image = new Image();
image.src = this.image();*/
var width = canvas.width;
var height = canvas.height;
if ( $( "#body" ).length ){
$( "#container" ).css('position','absolute');
$( "#container" ).css('visibility','hidden');
}
if ( $( "#mycanvas" ).length ) {
//$( "#mycanvas" ).remove();
/*
$('<canvas>').attr({
id: 'mycanvas'
}).css({
width: width + 'px',
height: height + 'px',
}).appendTo('#body');
$('#mycanvas').css("border","1px solid black");*/
//canvas already exists
}
else{
if ( $( "#body" ).length ){
$('<canvas>').attr({
id: 'mycanvas'
}).css({
width: width + 'px',
height: height + 'px',
}).appendTo('#body');
$('#mycanvas').css("border","1px solid black");
}
}
if ( $( "#body" ).length ){
var gesturableImg = new ImgTouchCanvas({
canvas: document.getElementById('mycanvas'),
path: this.image(),
desktop: true
});
}
}
}
/* code to enable touch events ends */
The code below is the function defined to add all the touch events. The mouse events were added again to modify them by comparing it with similar touch events.
/* Touch events function start GSoC 17*/
class ImgTouchCanvas {
constructor(options){
if( !options || !options.canvas || !options.path) {
throw 'ImgZoom constructor: missing arguments canvas or path';
}
this.canvas = options.canvas;
this.canvas.width = this.canvas.clientWidth;
this.canvas.height = this.canvas.clientHeight;
this.context = this.canvas.getContext('2d');
this.desktop = options.desktop || false; //non touch events
this.position = {
x: 0,
y: 0
};
this.scale = {
x: 0.5,
y: 0.5
};
this.imgTexture = new Image();
this.imgTexture.src = options.path;
this.lastZoomScale = null;
this.lastX = null;
this.lastY = null;
this.mdown = false; //desktop drag
this.init = false;
this.checkRequestAnimationFrame();
requestAnimationFrame(this.animate.bind(this));
this.setEventListeners();
}
animate() {
//set scale such as image cover all the canvas
if(!this.init) {
if(this.imgTexture.width) {
var scaleRatio = null;
if(this.canvas.clientWidth > this.canvas.clientHeight) {
scaleRatio = this.canvas.clientWidth / this.imgTexture.width;
}
else {
scaleRatio = this.canvas.clientHeight / this.imgTexture.height;
}
this.scale.x = scaleRatio;
this.scale.y = scaleRatio;
this.init = true;
}
}
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.drawImage(
this.imgTexture,
this.position.x, this.position.y,
this.scale.x * this.imgTexture.width,
this.scale.y * this.imgTexture.height);
requestAnimationFrame(this.animate.bind(this));
}
gesturePinchZoom(event) {
var zoom = false;
if( event.targetTouches.length >= 2 ) {
var p1 = event.targetTouches[0];
var p2 = event.targetTouches[1];
var zoomScale = Math.sqrt(Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)); //euclidian distance
if( this.lastZoomScale ) {
zoom = zoomScale - this.lastZoomScale;
}
this.lastZoomScale = zoomScale;
}
return zoom;
}
doZoom(zoom) {
if(!zoom) return;
//new scale
var currentScale = this.scale.x;
var newScale = this.scale.x + zoom/100;
//some helpers
var deltaScale = newScale - currentScale;
var currentWidth = (this.imgTexture.width * this.scale.x);
var currentHeight = (this.imgTexture.height * this.scale.y);
var deltaWidth = this.imgTexture.width*deltaScale;
var deltaHeight = this.imgTexture.height*deltaScale;
//by default scale doesnt change position and only add/remove pixel to right and bottom
//so we must move the image to the left to keep the image centered
//ex: coefX and coefY = 0.5 when image is centered <=> move image to the left 0.5x pixels added to the right
var canvasmiddleX = this.canvas.clientWidth / 2;
var canvasmiddleY = this.canvas.clientHeight / 2;
var xonmap = (-this.position.x) + canvasmiddleX;
var yonmap = (-this.position.y) + canvasmiddleY;
var coefX = -xonmap / (currentWidth);
var coefY = -yonmap / (currentHeight);
var newPosX = this.position.x + deltaWidth*coefX;
var newPosY = this.position.y + deltaHeight*coefY;
//edges cases
var newWidth = currentWidth + deltaWidth;
var newHeight = currentHeight + deltaHeight;
if( newWidth < this.canvas.clientWidth ) return;
if( newPosX > 0 ) { newPosX = 0; }
if( newPosX + newWidth < this.canvas.clientWidth ) { newPosX = this.canvas.clientWidth - newWidth;}
if( newHeight < this.canvas.clientHeight ) return;
if( newPosY > 0 ) { newPosY = 0; }
if( newPosY + newHeight < this.canvas.clientHeight ) { newPosY = this.canvas.clientHeight - newHeight; }
//finally affectations
this.scale.x = newScale;
this.scale.y = newScale;
this.position.x = newPosX;
this.position.y = newPosY;
}
doMove(relativeX, relativeY) {
if(this.lastX && this.lastY) {
var deltaX = relativeX - this.lastX;
var deltaY = relativeY - this.lastY;
var currentWidth = (this.imgTexture.width * this.scale.x);
var currentHeight = (this.imgTexture.height * this.scale.y);
this.position.x += deltaX;
this.position.y += deltaY;
//edge cases
if( this.position.x > 0 ) {
this.position.x = 0;
}
else if( this.position.x + currentWidth < this.canvas.clientWidth ) {
this.position.x = this.canvas.clientWidth - currentWidth;
}
if( this.position.y > 0 ) {
this.position.y = 0;
}
else if( this.position.y + currentHeight < this.canvas.clientHeight ) {
this.position.y = this.canvas.clientHeight - currentHeight;
}
}
this.lastX = relativeX;
this.lastY = relativeY;
}
setEventListeners() {
// touch
this.canvas.addEventListener('touchstart', function(e) {
this.lastX = null;
this.lastY = null;
this.lastZoomScale = null;
}.bind(this));
this.canvas.addEventListener('touchmove', function(e) {
e.preventDefault();
if(e.targetTouches.length == 2) { //pinch
this.doZoom(this.gesturePinchZoom(e));
}
else if(e.targetTouches.length == 1) {
var relativeX = e.targetTouches[0].pageX - this.canvas.getBoundingClientRect().left;
var relativeY = e.targetTouches[0].pageY - this.canvas.getBoundingClientRect().top;
this.doMove(relativeX, relativeY);
}
}.bind(this));
if(this.desktop) {
// keyboard+mouse
window.addEventListener('keyup', function(e) {
if(e.keyCode == 187 || e.keyCode == 61) { //+
this.doZoom(5);
}
else if(e.keyCode == 54) {//-
this.doZoom(-5);
}
}.bind(this));
window.addEventListener('mousedown', function(e) {
this.mdown = true;
this.lastX = null;
this.lastY = null;
}.bind(this));
window.addEventListener('mouseup', function(e) {
this.mdown = false;
}.bind(this));
window.addEventListener('mousemove', function(e) {
var relativeX = e.pageX - this.canvas.getBoundingClientRect().left;
var relativeY = e.pageY - this.canvas.getBoundingClientRect().top;
if(e.target == this.canvas && this.mdown) {
this.doMove(relativeX, relativeY);
}
if(relativeX <= 0 || relativeX >= this.canvas.clientWidth || relativeY <= 0 || relativeY >= this.canvas.clientHeight) {
this.mdown = false;
}
}.bind(this));
}
}
checkRequestAnimationFrame() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}
};
/* touch event function ends*/