mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-07-24 20:59:16 -04:00
206 lines
6.9 KiB
JavaScript
206 lines
6.9 KiB
JavaScript
|
/**
|
||
|
* @license
|
||
|
* Visual Blocks Editor
|
||
|
*
|
||
|
* Copyright 2016 Google Inc.
|
||
|
* https://developers.google.com/blockly/
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @fileoverview A class that manages a surface for dragging blocks. When a
|
||
|
* block drag is started, we move the block (and children) to a separate dom
|
||
|
* element that we move around using translate3d. At the end of the drag, the
|
||
|
* blocks are put back in into the svg they came from. This helps performance by
|
||
|
* avoiding repainting the entire svg on every mouse move while dragging blocks.
|
||
|
* @author picklesrus
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
goog.provide('Blockly.BlockDragSurfaceSvg');
|
||
|
goog.require('Blockly.utils');
|
||
|
goog.require('goog.asserts');
|
||
|
goog.require('goog.math.Coordinate');
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Class for a drag surface for the currently dragged block. This is a separate
|
||
|
* SVG that contains only the currently moving block, or nothing.
|
||
|
* @param {!Element} container Containing element.
|
||
|
* @constructor
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg = function(container) {
|
||
|
/**
|
||
|
* @type {!Element}
|
||
|
* @private
|
||
|
*/
|
||
|
this.container_ = container;
|
||
|
this.createDom();
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* The SVG drag surface. Set once by Blockly.BlockDragSurfaceSvg.createDom.
|
||
|
* @type {Element}
|
||
|
* @private
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.SVG_ = null;
|
||
|
|
||
|
/**
|
||
|
* This is where blocks live while they are being dragged if the drag surface
|
||
|
* is enabled.
|
||
|
* @type {Element}
|
||
|
* @private
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.dragGroup_ = null;
|
||
|
|
||
|
/**
|
||
|
* Containing HTML element; parent of the workspace and the drag surface.
|
||
|
* @type {Element}
|
||
|
* @private
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.container_ = null;
|
||
|
|
||
|
/**
|
||
|
* Cached value for the scale of the drag surface.
|
||
|
* Used to set/get the correct translation during and after a drag.
|
||
|
* @type {number}
|
||
|
* @private
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.scale_ = 1;
|
||
|
|
||
|
/**
|
||
|
* Create the drag surface and inject it into the container.
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.createDom = function() {
|
||
|
if (this.SVG_) {
|
||
|
return; // Already created.
|
||
|
}
|
||
|
this.SVG_ = Blockly.utils.createSvgElement('svg', {
|
||
|
'xmlns': Blockly.SVG_NS,
|
||
|
'xmlns:html': Blockly.HTML_NS,
|
||
|
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
||
|
'version': '1.1',
|
||
|
'class': 'blocklyBlockDragSurface'
|
||
|
}, this.container_);
|
||
|
this.dragGroup_ = Blockly.utils.createSvgElement('g', {}, this.SVG_);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Set the SVG blocks on the drag surface's group and show the surface.
|
||
|
* Only one block group should be on the drag surface at a time.
|
||
|
* @param {!Element} blocks Block or group of blocks to place on the drag
|
||
|
* surface.
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.setBlocksAndShow = function(blocks) {
|
||
|
goog.asserts.assert(this.dragGroup_.childNodes.length == 0,
|
||
|
'Already dragging a block.');
|
||
|
// appendChild removes the blocks from the previous parent
|
||
|
this.dragGroup_.appendChild(blocks);
|
||
|
this.SVG_.style.display = 'block';
|
||
|
// This allows blocks to be dragged outside of the blockly svg space.
|
||
|
// This should be reset to hidden at the end of the block drag.
|
||
|
// Note that this behavior is different from blockly where block disappear
|
||
|
// "under" the blockly area.
|
||
|
var injectionDiv = document.getElementsByClassName('injectionDiv')[0];
|
||
|
injectionDiv.style.overflow = 'visible';
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Translate and scale the entire drag surface group to keep in sync with the
|
||
|
* workspace.
|
||
|
* @param {number} x X translation
|
||
|
* @param {number} y Y translation
|
||
|
* @param {number} scale Scale of the group.
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.translateAndScaleGroup = function(x, y, scale) {
|
||
|
this.scale_ = scale;
|
||
|
// This is a work-around to prevent a the blocks from rendering
|
||
|
// fuzzy while they are being dragged on the drag surface.
|
||
|
x = x.toFixed(0);
|
||
|
y = y.toFixed(0);
|
||
|
this.dragGroup_.setAttribute('transform', 'translate('+ x + ','+ y + ')' +
|
||
|
' scale(' + scale + ')');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Translate the entire drag surface during a drag.
|
||
|
* We translate the drag surface instead of the blocks inside the surface
|
||
|
* so that the browser avoids repainting the SVG.
|
||
|
* Because of this, the drag coordinates must be adjusted by scale.
|
||
|
* @param {number} x X translation for the entire surface.
|
||
|
* @param {number} y Y translation for the entire surface.
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.translateSurface = function(x, y) {
|
||
|
var transform;
|
||
|
x *= this.scale_;
|
||
|
y *= this.scale_;
|
||
|
// This is a work-around to prevent a the blocks from rendering
|
||
|
// fuzzy while they are being dragged on the drag surface.
|
||
|
x = x.toFixed(0);
|
||
|
y = y.toFixed(0);
|
||
|
transform =
|
||
|
'transform: translate3d(' + x + 'px, ' + y + 'px, 0px); display: block;';
|
||
|
this.SVG_.setAttribute('style', transform);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Reports the surface translation in scaled workspace coordinates.
|
||
|
* Use this when finishing a drag to return blocks to the correct position.
|
||
|
* @return {!goog.math.Coordinate} Current translation of the surface.
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.getSurfaceTranslation = function() {
|
||
|
var xy = Blockly.utils.getRelativeXY(this.SVG_);
|
||
|
return new goog.math.Coordinate(xy.x / this.scale_, xy.y / this.scale_);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Provide a reference to the drag group (primarily for
|
||
|
* BlockSvg.getRelativeToSurfaceXY).
|
||
|
* @return {Element} Drag surface group element.
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.getGroup = function() {
|
||
|
return this.dragGroup_;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Get the current blocks on the drag surface, if any (primarily
|
||
|
* for BlockSvg.getRelativeToSurfaceXY).
|
||
|
* @return {!Element|undefined} Drag surface block DOM element, or undefined
|
||
|
* if no blocks exist.
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.getCurrentBlock = function() {
|
||
|
return this.dragGroup_.firstChild;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Clear the group and hide the surface; move the blocks off onto the provided
|
||
|
* element.
|
||
|
* @param {!Element} newSurface Surface the dragging blocks should be moved to.
|
||
|
*/
|
||
|
Blockly.BlockDragSurfaceSvg.prototype.clearAndHide = function(newSurface) {
|
||
|
// appendChild removes the node from this.dragGroup_
|
||
|
newSurface.appendChild(this.getCurrentBlock());
|
||
|
this.SVG_.style.display = 'none';
|
||
|
// Reset the overflow property back to hidden so that nothing appears outside
|
||
|
// of the blockly area.
|
||
|
// Note that this behavior is different from blockly. See note in
|
||
|
// setBlocksAndShow.
|
||
|
var injectionDiv = document.getElementsByClassName('injectionDiv')[0];
|
||
|
injectionDiv.style.overflow = 'hidden';
|
||
|
|
||
|
goog.asserts.assert(this.dragGroup_.childNodes.length == 0,
|
||
|
'Drag group was not cleared.');
|
||
|
};
|