From c14951c9284b3734fd351655ba874bb859bb9603 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Sun, 20 Sep 2015 14:28:39 -0700 Subject: [PATCH] Add disconnect sound and animation. --- core/block_svg.js | 91 ++++++++++++++++++++++++++++++++++++++++--- core/inject.js | 4 ++ media/disconnect.mp3 | Bin 0 -> 1586 bytes media/disconnect.ogg | Bin 0 -> 4404 bytes media/disconnect.wav | Bin 0 -> 1312 bytes 5 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 media/disconnect.mp3 create mode 100644 media/disconnect.ogg create mode 100644 media/disconnect.wav diff --git a/core/block_svg.js b/core/block_svg.js index 7afb52169..9650a7c8c 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -187,6 +187,7 @@ Blockly.BlockSvg.onMouseMoveWrapper_ = null; * @private */ Blockly.BlockSvg.terminateDrag_ = function() { + Blockly.BlockSvg.disconnectUiStop_(); if (Blockly.BlockSvg.onMouseUpWrapper_) { Blockly.unbindEvent_(Blockly.BlockSvg.onMouseUpWrapper_); Blockly.BlockSvg.onMouseUpWrapper_ = null; @@ -692,6 +693,7 @@ Blockly.BlockSvg.prototype.onMouseMove_ = function(e) { var oldXY = this_.getRelativeToSurfaceXY(); var newXY = workspace_.moveDrag(e); + var group = this_.getSvgRoot(); if (Blockly.dragMode_ == 1) { // Still dragging within the sticky DRAG_RADIUS. var dr = goog.math.Coordinate.distance(oldXY, newXY) * workspace_.scale; @@ -699,8 +701,15 @@ Blockly.BlockSvg.prototype.onMouseMove_ = function(e) { // Switch to unrestricted dragging. Blockly.dragMode_ = 2; Blockly.longStop_(); - // Push this block to the very top of the stack. - this_.setParent(null); + group.translate_ = ''; + group.skew_ = ''; + if (this_.parentBlock_) { + // Push this block to the very top of the stack. + this_.setParent(null); + if (workspace_.scale >= 1) { + this_.disconnectUiEffect(); + } + } this_.setDragging_(true); workspace_.recordDeleteAreas(); } @@ -709,8 +718,8 @@ Blockly.BlockSvg.prototype.onMouseMove_ = function(e) { // Unrestricted dragging. var dx = oldXY.x - this_.dragStartXY_.x; var dy = oldXY.y - this_.dragStartXY_.y; - this_.getSvgRoot().setAttribute('transform', - 'translate(' + newXY.x + ',' + newXY.y + ')'); + group.translate_ = 'translate(' + newXY.x + ',' + newXY.y + ')'; + group.setAttribute('transform', group.translate_ + group.skew_); // Drag all the nested bubbles. for (var i = 0; i < this_.draggedBubbles_.length; i++) { var commentData = this_.draggedBubbles_[i]; @@ -1126,10 +1135,82 @@ Blockly.BlockSvg.connectionUiStep_ = function(ripple, start, workspaceScale) { var closure = function() { Blockly.BlockSvg.connectionUiStep_(ripple, start, workspaceScale); }; - setTimeout(closure, 10); + Blockly.BlockSvg.disconnectUiStop_.pid_ = setTimeout(closure, 10); } }; +/** + * Play some UI effects (sound, animation) when disconnecting a block. + */ +Blockly.BlockSvg.prototype.disconnectUiEffect = function() { + this.workspace.playAudio('disconnect'); + // Horizontal distance for bottom of block to wiggle. + var DISPLACEMENT = 10; + // Scale magnitude of skew to height of block. + var height = this.getHeightWidth().height; + var magnitude = Math.atan(DISPLACEMENT / height) / Math.PI * 180; + if (this.RTL) { + magnitude *= -1; + } + // Start the animation. + Blockly.BlockSvg.disconnectUiStep_(this.svgGroup_, magnitude, new Date()); +}; + +/** + * Animate a brief wiggle of a disconnected block. + * @param {!Element} group SVG element to animate. + * @param {number} magnitude Maximum degrees skew (reversed for RTL). + * @param {!Date} start Date of animation's start. + * @private + */ +Blockly.BlockSvg.disconnectUiStep_ = function(group, magnitude, start) { + var DURATION = 200; // Milliseconds. + var WIGGLES = 3; // Half oscillations. + + var ms = (new Date()) - start; + var percent = ms / DURATION; + + if (percent > 1) { + group.skew_ = ''; + } else { + var skew = Math.round(Math.sin(percent * Math.PI * WIGGLES) * + (1 - percent) * magnitude); + group.skew_ = 'skewX(' + skew + ')'; + var closure = function() { + Blockly.BlockSvg.disconnectUiStep_(group, magnitude, start); + }; + Blockly.BlockSvg.disconnectUiStop_.group = group; + Blockly.BlockSvg.disconnectUiStop_.pid = setTimeout(closure, 10); + } + group.setAttribute('transform', group.translate_ + group.skew_); +}; + +/** + * Stop the disconnect UI animation immediately. + * @private + */ +Blockly.BlockSvg.disconnectUiStop_ = function() { + if (Blockly.BlockSvg.disconnectUiStop_.group) { + clearTimeout(Blockly.BlockSvg.disconnectUiStop_.pid); + var group = Blockly.BlockSvg.disconnectUiStop_.group + group.skew_ = ''; + group.setAttribute('transform', group.translate_); + Blockly.BlockSvg.disconnectUiStop_.group = null; + } +}; + +/** + * PID of disconnect UI animation. There can only be one at a time. + * @type {number} + */ +Blockly.BlockSvg.disconnectUiStop_.pid = 0; + +/** + * SVG group of wobbling block. There can only be one at a time. + * @type {Element} + */ +Blockly.BlockSvg.disconnectUiStop_.group = null; + /** * Change the colour of a block. */ diff --git a/core/inject.js b/core/inject.js index 026e308f3..a17e9c082 100644 --- a/core/inject.js +++ b/core/inject.js @@ -510,6 +510,10 @@ Blockly.init_ = function(mainWorkspace) { [options.pathToMedia + 'click.mp3', options.pathToMedia + 'click.wav', options.pathToMedia + 'click.ogg'], 'click'); + mainWorkspace.loadAudio_( + [options.pathToMedia + 'disconnect.mp3', + options.pathToMedia + 'disconnect.ogg', + options.pathToMedia + 'disconnect.wav'], 'disconnect'); mainWorkspace.loadAudio_( [options.pathToMedia + 'delete.mp3', options.pathToMedia + 'delete.ogg', diff --git a/media/disconnect.mp3 b/media/disconnect.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8cfaff6c06253a8b9bbe50dc0d5d05c4a25b6fb5 GIT binary patch literal 1586 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 literal 0 HcmV?d00001 diff --git a/media/disconnect.wav b/media/disconnect.wav new file mode 100644 index 0000000000000000000000000000000000000000..f6a7f84fba35b334619fd9d8ebaa97eee8d1b48e GIT binary patch literal 1312 zcmcgseQZ-z6hHUg_u8#x(2b8y#^A*dKy)jEIAja5u`y|~G1!_x0;YDf>xyM<+Rn*{ zh=wRSCL)=Ngn&vQ;*gI3<0B)+gb4#AgeV_IkkEZm#xSgX-Fxre>jUDy{^>nAIlpuA z%kSiz3jXNPqDP+(55zAgK>j|_mN=I9Mqa55vl=mG_FDF(kF~U>h0X0LMWj$E zeAwNeh~9}>`o#OT#HT`y<*v1A(uM5O?2bvctO?_+#<9ti*tV!I>hFCumJxpgGED2z z$+(H>)6FYzP>v0D^!*mS7On36aG>$;2Rvs?XXd%AP}VaOYpu-knGlSZ-7D?M>FMlW zJLDOu6uo2ijIYVGTFcW;TtqKW&g=hIK5PJroh z^TTVg1@|fk#Ybb6qo~t()>LR#QY0b3>?2#^M~0gd&ni>-B_Wi$Y*c<)yLmy1Lui3- z=^6Eca#7i#{-hT@|5Hh<$;z3FOX*OfT6>1T0BX@Xamn+Z_{)1B)iL}p*p;jTp?S?I-G_&_zE_p zJy2g(sj^AksQpeu{5cfH5z z57}S*Hta`nbO0%EkULo+{X*NJRZ|~3#s!o_EAeLBfHUz|XgpdEjMwrX*=W{9_tMKW z#MX0yw&Np&lK06RycqJBPis}XRU`d@ZHH-i5!p%Ncmo>b$Cyklh*mChK@fAQ<#Dc%^cm zTME?aAz173uaT-FzEyAO%;GACNoQ3@Tp_