UNPKG

escher-vis

Version:

Escher: A Web Application for Building, Sharing, and Embedding Data-Rich Visualizations of Biological Pathways

258 lines (230 loc) 8.52 kB
/** * Canvas. Defines a canvas that accepts drag/zoom events and can be resized. * Canvas(selection, x, y, width, height) * Adapted from http://bl.ocks.org/mccannf/1629464. */ var utils = require('./utils') var CallbackManager = require('./CallbackManager') var d3_drag = require('d3-drag').drag var d3_selection = require('d3-selection') var _ = require('underscore') var Canvas = utils.make_class() Canvas.prototype = { init: init, toggle_resize: toggle_resize, setup: setup, size_and_location: size_and_location } module.exports = Canvas function init (selection, size_and_location) { this.selection = selection this.x = size_and_location.x this.y = size_and_location.y this.width = size_and_location.width this.height = size_and_location.height // enable by default this.resize_enabled = true // set up the callbacks this.callback_manager = new CallbackManager() this.setup() } /** * Turn the resize on or off */ function toggle_resize (on_off) { if (_.isUndefined(on_off)) on_off = !this.resize_enabled if (on_off) { this.selection.selectAll('.drag-rect') .style('pointer-events', 'auto') } else { this.selection.selectAll('.drag-rect') .style('pointer-events', 'none') } } function setup () { var self = this var extent = { x: this.width, y: this.height } var dragbar_width = 100 var mouse_node_mult = 10 var new_sel = this.selection.append('g') .classed('canvas-group', true) .data([ { x: this.x, y: this.y } ]) var mouse_node = new_sel.append('rect') .attr('id', 'mouse-node') .attr('width', this.width * mouse_node_mult) .attr('height', this.height * mouse_node_mult) .attr('transform', 'translate(' + [ self.x - this.width*mouse_node_mult/2, self.y - this.height*mouse_node_mult/2 ] + ')') .attr('pointer-events', 'all') this.mouse_node = mouse_node var rect = new_sel.append('rect') .attr('id', 'canvas') .attr('width', this.width) .attr('height', this.height) .attr('transform', 'translate(' + [ self.x, self.y ] + ')') var drag_right = d3_drag() // TODO do we need these? in d3 v4, origin is now subject // .origin(Object) .on('start', stop_propagation) .on('drag', rdragresize), drag_left = d3_drag() // .origin(Object) .on('start', stop_propagation) .on('drag', ldragresize), drag_top = d3_drag() // .origin(Object) .on('start', stop_propagation) .on('drag', tdragresize), drag_bottom = d3_drag() // .origin(Object) .on('start', stop_propagation) .on('drag', bdragresize) var left = new_sel.append('rect') .classed('drag-rect', true) .attr('transform', function(d) { return 'translate('+[ d.x - (dragbar_width/2), d.y + (dragbar_width/2) ]+')' }) .attr('height', this.height - dragbar_width) .attr('id', 'dragleft') .attr('width', dragbar_width) .attr('cursor', 'ew-resize') .classed('resize-rect', true) .call(drag_left) var right = new_sel.append('rect') .classed('drag-rect', true) .attr('transform', function(d) { return 'translate('+[ d.x + self.width - (dragbar_width/2), d.y + (dragbar_width/2) ]+')' }) .attr('id', 'dragright') .attr('height', this.height - dragbar_width) .attr('width', dragbar_width) .attr('cursor', 'ew-resize') .classed('resize-rect', true) .call(drag_right) var top = new_sel.append('rect') .classed('drag-rect', true) .attr('transform', function(d) { return 'translate('+[ d.x + (dragbar_width/2), d.y - (dragbar_width/2) ]+')' }) .attr('height', dragbar_width) .attr('id', 'dragtop') .attr('width', this.width - dragbar_width) .attr('cursor', 'ns-resize') .classed('resize-rect', true) .call(drag_top) var bottom = new_sel.append('rect') .classed('drag-rect', true) .attr('transform', function(d) { return 'translate('+[ d.x + (dragbar_width/2), d.y + self.height - (dragbar_width/2) ]+')' }) .attr('id', 'dragbottom') .attr('height', dragbar_width) .attr('width', this.width - dragbar_width) .attr('cursor', 'ns-resize') .classed('resize-rect', true) .call(drag_bottom) // definitions function stop_propagation () { d3_selection.event.sourceEvent.stopPropagation() } function transform_string (x, y, current_transform) { var tr = utils.d3_transform_catch(current_transform), translate = tr.translate if (x !== null) translate[0] = x if (y !== null) translate[1] = y return 'translate(' + translate + ')' } function ldragresize (d) { var oldx = d.x d.x = Math.min(d.x + self.width - (dragbar_width / 2), d3_selection.event.x) self.x = d.x self.width = self.width + (oldx - d.x) left.attr('transform', function(d) { return transform_string(d.x - (dragbar_width / 2), null, left.attr('transform')) }) mouse_node.attr('transform', function(d) { return transform_string(d.x, null, mouse_node.attr('transform')) }).attr('width', self.width*mouse_node_mult) rect.attr('transform', function(d) { return transform_string(d.x, null, rect.attr('transform')) }).attr('width', self.width) top.attr('transform', function(d) { return transform_string(d.x + (dragbar_width/2), null, top.attr('transform')) }).attr('width', self.width - dragbar_width) bottom.attr('transform', function(d) { return transform_string(d.x + (dragbar_width/2), null, bottom.attr('transform')) }).attr('width', self.width - dragbar_width) self.callback_manager.run('resize') } function rdragresize (d) { d3_selection.event.sourceEvent.stopPropagation() var dragx = Math.max(d.x + (dragbar_width/2), d.x + self.width + d3_selection.event.dx) //recalculate width self.width = dragx - d.x //move the right drag handle right.attr('transform', function(d) { return transform_string(dragx - (dragbar_width/2), null, right.attr('transform')) }) //resize the drag rectangle //as we are only resizing from the right, the x coordinate does not need to change mouse_node.attr('width', self.width*mouse_node_mult) rect.attr('width', self.width) top.attr('width', self.width - dragbar_width) bottom.attr('width', self.width - dragbar_width) self.callback_manager.run('resize') } function tdragresize (d) { d3_selection.event.sourceEvent.stopPropagation() var oldy = d.y d.y = Math.min(d.y + self.height - (dragbar_width / 2), d3_selection.event.y) self.y = d.y self.height = self.height + (oldy - d.y) top.attr('transform', function(d) { return transform_string(null, d.y - (dragbar_width / 2), top.attr('transform')) }) mouse_node.attr('transform', function(d) { return transform_string(null, d.y, mouse_node.attr('transform')) }).attr('width', self.height*mouse_node_mult) rect.attr('transform', function(d) { return transform_string(null, d.y, rect.attr('transform')) }).attr('height', self.height) left.attr('transform', function(d) { return transform_string(null, d.y + (dragbar_width/2), left.attr('transform')) }).attr('height', self.height - dragbar_width) right.attr('transform', function(d) { return transform_string(null, d.y + (dragbar_width/2), right.attr('transform')) }).attr('height', self.height - dragbar_width) self.callback_manager.run('resize') } function bdragresize(d) { d3_selection.event.sourceEvent.stopPropagation() var dragy = Math.max(d.y + (dragbar_width/2), d.y + self.height + d3_selection.event.dy) //recalculate width self.height = dragy - d.y //move the right drag handle bottom.attr('transform', function(d) { return transform_string(null, dragy - (dragbar_width/2), bottom.attr('transform')) }) //resize the drag rectangle //as we are only resizing from the right, the x coordinate does not need to change mouse_node.attr('height', self.height*mouse_node_mult) rect.attr('height', self.height) left.attr('height', self.height - dragbar_width) right.attr('height', self.height - dragbar_width) self.callback_manager.run('resize') } } function size_and_location () { return { x: this.x, y: this.y, width: this.width, height: this.height } }