diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py index ff6a77c..f248387 100644 --- a/pygal/graph/graph.py +++ b/pygal/graph/graph.py @@ -49,19 +49,21 @@ class Graph(BaseGraph): self.graph_node, class_="plot tooltip-overlay", transform="translate(%d, %d)" % ( self.margin.left, self.margin.top)) - self.tooltip_node = self.svg.node(tooltip_overlay, id="tooltip") - self.svg.node( - self.tooltip_node, 'animateTransform', - attributeName='transform', - attributeType='XML', - type='translate', - dur='1.5s', - end='indefinite', - begin='indefinite', - from_="0 0", - to="100 100", - additive="sum", - repeatCount='indefinite') + self.tooltip_node = self.svg.node(tooltip_overlay, id="tooltip", + transform='translate(0 0)') + # self.svg.node( + # self.tooltip_node, 'animateTransform', + # id="tooltip-slide", + # attributeName='transform', + # attributeType='XML', + # type='translate', + # calcMode='spline', + # dur='100ms', + # end='indefinite', + # begin='indefinite', + # from_="0 0", + # to="100 100", + # fill='freeze') self.svg.node(self.tooltip_node, 'rect', id="tooltip-box", diff --git a/pygal/js/graph.coffee b/pygal/js/graph.coffee index 4efca95..a12fa44 100644 --- a/pygal/js/graph.coffee +++ b/pygal/js/graph.coffee @@ -4,6 +4,36 @@ padding = 5 tooltip_timeout = 0 tooltip_font_size = parseInt("{{ font_sizes.tooltip }}") +class Queue + constructor: (@delay) -> + @queue = [] + @running = false + + add: (f, args...) -> + @queue.push f: f, a: args + if (!@running) + @running = true + @_back() + + _run: (f) -> + if(!f) + @running = false + else + setTimeout (=> + f.f f.a... + @_back() + ), @delay + + _back: -> + @_run @queue.shift() + + clear: -> + if @running + @queue = [] + @running = false + +tooltip_anim_Q = new Queue 1 + has_class = (e, class_name) -> return if not e cn = e.getAttribute('class').split(' ') @@ -27,6 +57,9 @@ rm_class = (e, class_name) -> cn.splice(i, 1) e.setAttribute('class', cn.join(' ')) +width = (e) -> (e.getBBox() and e.getBBox().width) or e.offsetWidth +height = (e) -> (e.getBBox() and e.getBBox().height) or e.offsetHeight + svg = (tag) -> document.createElementNS('http://www.w3.org/2000/svg', tag) activate = (elements...) -> @@ -47,33 +80,49 @@ hover = (elts, over, out) -> elt.addEventListener('mouseout', out.bind(elt) , false) tooltip = (elt) -> + tooltip_anim_Q.clear() clearTimeout(tooltip_timeout) _tooltip = __('tooltip') - # _tooltip.setAttribute('display', 'inline') + _tooltip.setAttribute('display', 'inline') _text = _tooltip.getElementsByTagName('text')[0] _rect = _tooltip.getElementsByTagName('rect')[0] value = elt.nextElementSibling _text.textContent = value.textContent - w = _text.offsetWidth + 2 * padding - h = _text.offsetHeight + 2 * padding + w = width(_text) + 2 * padding + h = height(_text) + 2 * padding _rect.setAttribute('width', w) _rect.setAttribute('height', h) _text.setAttribute('x', padding) _text.setAttribute('y', padding + tooltip_font_size) x_elt = value.nextElementSibling y_elt = x_elt.nextElementSibling - x = x_elt.textContent + x = parseInt(x_elt.textContent) if has_class(x_elt, 'centered') x -= w / 2 - y = y_elt.textContent + y = parseInt(y_elt.textContent) if has_class(y_elt, 'centered') y -= h / 2 - _tooltip.setAttribute('transform', "translate(#{x} #{y})") -untooltip = -> 0 + [current_x, current_y] = (parseInt(s) for s in _tooltip.getAttribute('transform').replace('translate(', '').replace(')', '').split ' ') + return if current_x == x and current_y == y + step = 20 + x_step = (x - current_x) / step + y_step = (y - current_y) / step + anim_x = current_x + anim_y = current_y + for i in [0..step] + anim_x += x_step + anim_y += y_step + tooltip_anim_Q.add ((_x, _y) -> + _tooltip.setAttribute('transform', "translate(#{_x} #{_y})")), anim_x, anim_y + tooltip_anim_Q.add ((_x, _y) -> + _tooltip.setAttribute('transform', "translate(#{_x} #{_y})")), x, y + + +untooltip = -> tooltip_timeout = setTimeout (-> - __('tooltip').setfAttribute('display', 'none')), 1000 + __('tooltip').setAttribute('display', 'none')), 1000 @svg_load = -> for text in _('.text-overlay .series') diff --git a/pygal/js/graph.js b/pygal/js/graph.js index 7b8cd88..a41d232 100644 --- a/pygal/js/graph.js +++ b/pygal/js/graph.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 1.2.1-pre (function() { - var activate, add_class, deactivate, has_class, hover, padding, rm_class, svg, tooltip, tooltip_font_size, tooltip_timeout, untooltip, _, __, + var Queue, activate, add_class, deactivate, has_class, height, hover, padding, rm_class, svg, tooltip, tooltip_anim_Q, tooltip_font_size, tooltip_timeout, untooltip, width, _, __, __slice = [].slice; _ = function(x) { @@ -17,6 +17,58 @@ tooltip_font_size = parseInt("{{ font_sizes.tooltip }}"); + Queue = (function() { + + Queue.name = 'Queue'; + + function Queue(delay) { + this.delay = delay; + this.queue = []; + this.running = false; + } + + Queue.prototype.add = function() { + var args, f; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + this.queue.push({ + f: f, + a: args + }); + if (!this.running) { + this.running = true; + return this._back(); + } + }; + + Queue.prototype._run = function(f) { + var _this = this; + if (!f) { + return this.running = false; + } else { + return setTimeout((function() { + f.f.apply(f, f.a); + return _this._back(); + }), this.delay); + } + }; + + Queue.prototype._back = function() { + return this._run(this.queue.shift()); + }; + + Queue.prototype.clear = function() { + if (this.running) { + this.queue = []; + return this.running = false; + } + }; + + return Queue; + + })(); + + tooltip_anim_Q = new Queue(1); + has_class = function(e, class_name) { var cls, cn, i, _i, _len; if (!e) return; @@ -47,6 +99,14 @@ return e.setAttribute('class', cn.join(' ')); }; + width = function(e) { + return (e.getBBox() && e.getBBox().width) || e.offsetWidth; + }; + + height = function(e) { + return (e.getBBox() && e.getBBox().height) || e.offsetHeight; + }; + svg = function(tag) { return document.createElementNS('http://www.w3.org/2000/svg', tag); }; @@ -93,30 +153,59 @@ }; tooltip = function(elt) { - var h, value, w, x, x_elt, y, y_elt, _rect, _text, _tooltip; + var anim_x, anim_y, current_x, current_y, h, i, s, step, value, w, x, x_elt, x_step, y, y_elt, y_step, _i, _rect, _ref, _text, _tooltip; + tooltip_anim_Q.clear(); clearTimeout(tooltip_timeout); _tooltip = __('tooltip'); + _tooltip.setAttribute('display', 'inline'); _text = _tooltip.getElementsByTagName('text')[0]; _rect = _tooltip.getElementsByTagName('rect')[0]; value = elt.nextElementSibling; _text.textContent = value.textContent; - w = _text.offsetWidth + 2 * padding; - h = _text.offsetHeight + 2 * padding; + w = width(_text) + 2 * padding; + h = height(_text) + 2 * padding; _rect.setAttribute('width', w); _rect.setAttribute('height', h); _text.setAttribute('x', padding); _text.setAttribute('y', padding + tooltip_font_size); x_elt = value.nextElementSibling; y_elt = x_elt.nextElementSibling; - x = x_elt.textContent; + x = parseInt(x_elt.textContent); if (has_class(x_elt, 'centered')) x -= w / 2; - y = y_elt.textContent; + y = parseInt(y_elt.textContent); if (has_class(y_elt, 'centered')) y -= h / 2; - return _tooltip.setAttribute('transform', "translate(" + x + " " + y + ")"); + _ref = (function() { + var _i, _len, _ref, _results; + _ref = _tooltip.getAttribute('transform').replace('translate(', '').replace(')', '').split(' '); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + s = _ref[_i]; + _results.push(parseInt(s)); + } + return _results; + })(), current_x = _ref[0], current_y = _ref[1]; + if (current_x === x && current_y === y) return; + step = 20; + x_step = (x - current_x) / step; + y_step = (y - current_y) / step; + anim_x = current_x; + anim_y = current_y; + for (i = _i = 0; 0 <= step ? _i <= step : _i >= step; i = 0 <= step ? ++_i : --_i) { + anim_x += x_step; + anim_y += y_step; + tooltip_anim_Q.add((function(_x, _y) { + return _tooltip.setAttribute('transform', "translate(" + _x + " " + _y + ")"); + }), anim_x, anim_y); + } + return tooltip_anim_Q.add((function(_x, _y) { + return _tooltip.setAttribute('transform', "translate(" + _x + " " + _y + ")"); + }), x, y); }; untooltip = function() { - return 0; + return tooltip_timeout = setTimeout((function() { + return __('tooltip').setAttribute('display', 'none'); + }), 1000); }; this.svg_load = function() {