UNPKG

d3-canvas-transition

Version:

Animated transitions for D3 selections on svg and canvas

344 lines (305 loc) 8.49 kB
import {map} from 'd3-collection'; import {StyleNode} from './attrs/style'; import setAttribute from './attrs/set'; import deque from './deque'; import {touch} from './draw'; import {NodeIterator} from './iterator'; import {canvasListener, canvasNodeListener, mouseEvents} from './events'; var namespace = 'canvas'; var canvasStyles = ['cursor']; /** * A proxy for a data entry on canvas * * It partially implements the Node Api (please pull request!) * https://developer.mozilla.org/en-US/docs/Web/API/Node * * It allows the use the d3-select and d3-transition libraries * on canvas joins */ export function CanvasElement (tagName, context) { var _deque, events = {}, text = ''; Object.defineProperties(this, { context: { get () { return context; } }, deque: { get () { if (!_deque) _deque = deque(); return _deque; } }, events: { get () { return events; } }, tagName: { get () { return tagName; } }, childNodes: { get () { return _deque ? _deque.list() : []; } }, firstChild: { get () { return _deque ? _deque._head : null; } }, lastChild: { get () { return _deque ? _deque._tail : null; } }, parentNode: { get() { return this._parent; } }, previousSibling: { get () { return this._prev; } }, nextSibling: { get () { return this._next; } }, namespaceURI: { get () { return namespace; } }, textContent: { get () { return text; }, set (value) { text = ''+value; touch(this, 1); } }, clientLeft: { get () { return context.canvas.clientLeft; } }, clientTop: { get () { return context.canvas.clientTop; } }, clientWidth: { get () { return context.canvas.clientWidth; } }, clientHeight: { get () { return context.canvas.clientHeight; } }, rootNode: { get () { return this.context._rootElement; } }, // // Canvas Element properties countNodes: { get () { return _deque ? _deque._length : 0; } }, factor: { get () { return this.context._factor; } } }); } CanvasElement.prototype = { querySelectorAll (selector) { return this.countNodes ? select(selector, this, []) : []; }, querySelector (selector) { if (this.countNodes) return select(selector, this); }, createElementNS (namespaceURI, qualifiedName) { return new CanvasElement(qualifiedName, this.context); }, hasChildNodes () { return this.countNodes > 0; }, contains (child) { while(child) { if(child._parent == this) return true; child = child._parent; } return false; }, appendChild (child) { return this.insertBefore(child); }, insertBefore (child, refChild) { if (child === this) throw Error('inserting self into children'); if (!(child instanceof CanvasElement)) throw Error('Cannot insert a non canvas element into a canvas element'); if (child._parent) child._parent.removeChild(child); this.deque.prepend(child, refChild); child._parent = this; touch(this, 1); return child; }, removeChild (child) { if (child._parent === this) { delete child._parent; this.deque.remove(child); touch(this, 1); return child; } }, setAttribute (attr, value) { if (attr === 'class') { this.class = value; } else if (attr === 'id') { this.id = value; } else { if (!this.attrs) this.attrs = map(); if (setAttribute(this, attr, value)) touch(this, 1); } }, removeAttribute (attr) { if (this.attrs) { this.attrs.remove(attr); touch(this, 1); } }, getAttribute (attr) { var value = this.attrs ? this.attrs.get(attr) : undefined; if (value === undefined && !this._parent) value = this.context.canvas[attr]; return value; }, addEventListener (type, listener) { var canvas = this.context.canvas; if (this.rootNode === this) { arguments[1] = canvasListener; canvas.addEventListener.apply(canvas, arguments); } else { arguments[0] = mouseEvents[type] || type; arguments[1] = canvasNodeListener; canvas.addEventListener.apply(canvas, arguments); } var listeners = this.events[type]; if (!listeners) this.events[type] = listeners = []; if (listeners.indexOf(listener) === -1) listeners.push(listener); }, removeEventListener (type, listener) { var listeners = this.events[type]; if (listeners) { var i = listeners.indexOf(listener); if (i > -1) listeners.splice(i, 1); } }, getBoundingClientRect () { return this.context.canvas.getBoundingClientRect(); }, // Canvas methods each (f) { if (this.countNodes) this.deque.each(f); }, getValue (attr) { var value = this.getAttribute(attr); if (value === undefined && this._parent) return this._parent.getValue(attr); return value; }, // Additional attribute functions removeProperty(name) { this.removeAttribute(name); }, setProperty(name, value) { if (canvasStyles.indexOf(name) > -1) this.context.canvas.style[name] = value; else this.setAttribute(name, value); }, getProperty(name) { return this.getAttribute(name); }, getPropertyValue (name) { return this.getAttribute(name); }, // Proxies to this object getComputedStyle (node) { return new StyleNode(node); }, get ownerDocument () { return this; }, get style () { return this; }, get defaultView () { return this; }, get document () { return this.rootNode; } }; function select(selector, node, selections) { var selectors = selector.split(' '), iterator = new NodeIterator(node), child = iterator.next(), classes, bits, tag, id, sel; for (let s=0; s<selectors.length; ++s) { selector = selectors[s]; if (selector === '*') { selector = {}; } else { if (selector.indexOf('#') > -1) { bits = selector.split('#'); tag = bits[0]; id = bits[1]; } else if (selector.indexOf('.') > -1) { bits = selector.split('.'); tag = bits[0]; classes = bits.splice(1).join(' '); } else tag = selector; selector = {tag, id, classes}; } selectors[s] = selector; } while (child) { for (let s=0; s<selectors.length; ++s) { sel = selectors[s]; if (!sel.tag || child.tagName === sel.tag) { if ((sel.id && child.id !== sel.id) || (sel.classes && child.class !== sel.classes)) { // nothing to do } else if (selections) { selections.push(child); break; } else return child; } } child = iterator.next(); } return selections; }