Merge pull request #965 from K-ran/feature/minimap

Adding new minimap demo.
This commit is contained in:
Rachel Fenichel
2017-04-03 11:30:49 -07:00
committed by GitHub
3 changed files with 396 additions and 0 deletions

BIN
demos/minimap/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

90
demos/minimap/index.html Normal file
View File

@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Blockly Demo: Minimap </title>
<script src="../../blockly_compressed.js"></script>
<script src="../../blocks_compressed.js"></script>
<script src="../../msg/js/en.js"></script>
<script src="minimap.js"></script>
<style>
body {
background-color: #fff;
font-family: sans-serif;
}
h1 {
font-weight: normal;
font-size: 140%;
}
</style>
</head>
<body>
<h1><a href="https://developers.google.com/blockly/">Blockly</a> &gt;
<a href="../index.html">Demos</a> &gt; Minimap</h1>
<p>This is a simple demo showing how a minimap can be implemented.</p>
<table width="100%">
<tr>
<td>
<div id="masterDiv" style="height: 480px; width: 900px;"></div>
</td>
<td>
<div id="mapDiv" style="height: 480px; width: 200px;"></div>
</td>
</tr>
</table>
<xml id="toolbox" style="display: none">
<block type="controls_if"></block>
<block type="logic_compare"></block>
<block type="controls_repeat_ext"></block>
<block type="math_number"></block>
<block type="math_arithmetic"></block>
<block type="text"></block>
<block type="text_print"></block>
<block type="variables_get"><field name="VAR">i</field></block>
<block type="variables_get"><field name="VAR">j</field></block>
<block type="variables_get"><field name="VAR">k</field></block>
</xml>
<script>
// Inject master workspace.
var masterWorkspace = Blockly.inject('masterDiv',{
media: '../../media/',
scrollbars: true,
toolbox: document.getElementById('toolbox')
});
// Inject workspace for minimap.
var minimapWorkspace = Blockly.inject('mapDiv',
{media: '../../media/',
readOnly: true,
zoom:
{controls: false,
wheel: true,
startScale: 0.1, //you can change this accorting to your needs.
maxScale: 0.1,
minScale: 0.01
}});
// Initilizing the minimap.
Minimap.init(masterWorkspace,minimapWorkspace);
</script>
<style>
.minimap{
position:absolute;
}
.mapDragger{
cursor: move;
fill:rgb(0,0,255);
stroke-width:0.5;
stroke:rgb(0,0,0);
fill-opacity:0.1;
}
</style>
</body>
</html>

306
demos/minimap/minimap.js Normal file
View File

@@ -0,0 +1,306 @@
/**
* Blockly Demos: Code
*
* Copyright 2017 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 JavaScript for Blockly's Minimap demo.
* @author karnpurohit@gmail.com (Karan Purohit)
*/
'use strict';
/**
* Creating a seperate namespace for minimap.
*/
var Minimap = {};
/**
* Initilize the workspace and minimap.
* @param {Workspace} workspace The main workspace of the user.
* @param {Workspace} minimap The workspace that will be used as a minimap.
*/
Minimap.init = function(workspace, minimap){
this.workspace = workspace;
this.minimap = minimap;
//Adding scroll callback functionlity to vScroll and hScroll just for this demo.
//IMPORTANT: This should be changed when there is proper UI event handling
// api available and should be handled by workspace's event listeners.
this.workspace.scrollbar.vScroll.setHandlePosition = function(newPosition){
this.handlePosition_ = newPosition;
this.svgHandle_.setAttribute(this.positionAttribute_, this.handlePosition_);
// Code above is same as the original setHandlePosition function in core/scrollbar.js.
// New code starts from here.
// Get the absolutePosition.
var absolutePosition = (this.handlePosition_ / this.ratio_);
// Firing the scroll change listener.
Minimap.onScrollChange(absolutePosition, this.horizontal_);
};
// Adding call back for horizontal scroll.
this.workspace.scrollbar.hScroll.setHandlePosition = function(newPosition){
this.handlePosition_ = newPosition;
this.svgHandle_.setAttribute(this.positionAttribute_, this.handlePosition_);
// Code above is same as the original setHandlePosition function in core/scrollbar.js.
// New code starts from here.
// Get the absolutePosition.
var absolutePosition = (this.handlePosition_ / this.ratio_);
// Firing the scroll change listener.
Minimap.onScrollChange(absolutePosition, this.horizontal_);
};
// Required to stop a positive feedback loop when user clicks minimap
// and the scroll changes, which inturn may change minimap.
this.disableScrollChange = false;
// Listen to events on the main workspace.
this.workspace.addChangeListener(Minimap.mirrorEvent);
//Get rectangle bounding the minimap div.
this.rect = document.getElementById('mapDiv').getBoundingClientRect();
// Create a svg overlay on the top of mapDiv for the minimap.
this.svg = Blockly.utils.createSvgElement('svg', {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:html': 'http://www.w3.org/1999/xhtml',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'version': '1.1',
'height': this.rect.bottom-this.rect.top,
'width': this.rect.right-this.rect.left,
'class': 'minimap',
}, document.getElementById('mapDiv'));
this.svg.style.top = this.rect.top + 'px';
this.svg.style.left = this.rect.left + 'px';
// Creating a rectangle in the minimap that represents current view.
Blockly.utils.createSvgElement('rect', {
'width':100,
'height':100,
'class':'mapDragger'
}, this.svg);
// Rectangle in the minimap that represents current view.
this.mapDragger = this.svg.childNodes[0];
// Adding mouse events to the rectangle, to make it Draggable.
// Using Blockly.bindEvent_ to attach mouse/touch listeners.
Blockly.bindEvent_(this.mapDragger, "mousedown", null, Minimap.mousedown);
//When the window change, we need to resize the minimap window.
window.addEventListener('resize', Minimap.repositionMinimap);
// Mouse up event for the minimap.
this.svg.addEventListener('mouseup', Minimap.updateMapDragger);
//Boolen to check whether I am dragging the surface or not.
this.isDragging = false;
};
Minimap.mousedown = function(e){
// Using Blockly.bindEvent_ to attach mouse/touch listeners.
Minimap.mouseMoveBindData = Blockly.bindEvent_(document,"mousemove", null, Minimap.mousemove);
Minimap.mouseUpBindData = Blockly.bindEvent_(document,"mouseup", null, Minimap.mouseup);
Minimap.isDragging=true;
e.stopPropagation();
};
Minimap.mouseup = function(e){
Minimap.isDragging = false;
// Removing listeners.
Blockly.unbindEvent_(Minimap.mouseUpBindData);
Blockly.unbindEvent_(Minimap.mouseMoveBindData);
Minimap.updateMapDragger(e);
e.stopPropagation();
};
Minimap.mousemove = function(e){
if(Minimap.isDragging){
Minimap.updateMapDragger(e);
e.stopPropagation();
}
};
/**
* Initilize the workspace and minimap.
* @param {Event} event Event that triggered in the main workspace.
*/
Minimap.mirrorEvent = function(event){
if (event.type == Blockly.Events.UI) {
return; // Don't mirror UI events.
}
// Convert event to JSON. This could then be transmitted across the net.
var json = event.toJson();
// Convert JSON back into an event, then execute it.
var minimapEvent = Blockly.Events.fromJson(json, Minimap.minimap);
minimapEvent.run(true);
Minimap.scaleMinimap();
Minimap.setDraggerHeight();
Minimap.setDraggerWidth();
};
/**
* Called when window is resized. Repositions the minimap overlay.
*/
Minimap.repositionMinimap = function(){
Minimap.rect = document.getElementById('mapDiv').getBoundingClientRect();
Minimap.svg.style.top = Minimap.rect.top + 'px';
Minimap.svg.style.left = Minimap.rect.left + 'px';
};
/**
* Updates the rectangle's height .
*/
Minimap.setDraggerHeight = function(){
var workspaceMetrics = Minimap.workspace.getMetrics();
var draggerHeight = (workspaceMetrics.viewHeight / Minimap.workspace.scale) * Minimap.minimap.scale;
// It's zero when first block is placed.
if(draggerHeight == 0){
return;
}
Minimap.mapDragger.setAttribute("height", draggerHeight);
};
/**
* Updates the rectangle's width.
*/
Minimap.setDraggerWidth = function(){
var workspaceMetrics = Minimap.workspace.getMetrics();
var draggerWidth = (workspaceMetrics.viewWidth / Minimap.workspace.scale) * Minimap.minimap.scale;
// It's zero when first block is placed.
if(draggerWidth == 0){
return;
}
Minimap.mapDragger.setAttribute("width", draggerWidth);
};
/**
* Updates the overall position of the viewport of the minimap by appropriately
* using translate functions.
*/
Minimap.scaleMinimap = function(){
var minimapBoundingBox = Minimap.minimap.getBlocksBoundingBox();
var workspaceBoundingBox = Minimap.workspace.getBlocksBoundingBox();
var workspaceMetrics = Minimap.workspace.getMetrics();
var minimapMetrics = Minimap.minimap.getMetrics();
//Scaling the mimimap such that all the blocks can be seen in the viewport.
//This padding is default because this is how to scrollbar(in main workspace) is implemented.
var topPadding = (workspaceMetrics.viewHeight) * Minimap.minimap.scale / (2 * Minimap.workspace.scale);
var sidePadding = (workspaceMetrics.viewWidth) * Minimap.minimap.scale / (2 * Minimap.workspace.scale);
// If actual padding is more than half view ports height, change it to actual padding.
if((workspaceBoundingBox.y * Minimap.workspace.scale - workspaceMetrics.contentTop)
* Minimap.minimap.scale / Minimap.workspace.scale > topPadding){
topPadding = (workspaceBoundingBox.y * Minimap.workspace.scale - workspaceMetrics.contentTop)
* Minimap.minimap.scale / Minimap.workspace.scale;
}
// If actual padding is more than half view ports height, change it to actual padding.
if((workspaceBoundingBox.x * Minimap.workspace.scale - workspaceMetrics.contentLeft)
* Minimap.minimap.scale / Minimap.workspace.scale > sidePadding){
sidePadding = (workspaceBoundingBox.x * Minimap.workspace.scale - workspaceMetrics.contentLeft)
* Minimap.minimap.scale / Minimap.workspace.scale;
}
var scalex = (minimapMetrics.viewWidth - 2 * sidePadding) / minimapBoundingBox.width;
var scaley = (minimapMetrics.viewHeight - 2 * topPadding) / minimapBoundingBox.height;
Minimap.minimap.setScale(Math.min(scalex, scaley));
// Translating the minimap.
Minimap.minimap.translate( - minimapMetrics.contentLeft * Minimap.minimap.scale + sidePadding,
- minimapMetrics.contentTop * Minimap.minimap.scale + topPadding);
};
/**
* Handles the onclick event on the minimapBoundingBox. Changes mapDraggers position.
* @param {Event} e Event from the mouse click.
*/
Minimap.updateMapDragger = function(e){
var y = e.clientY;
var x = e.clientX;
var draggerHeight = Minimap.mapDragger.getAttribute("height");
var draggerWidth = Minimap.mapDragger.getAttribute("width");
var finalY = y - Minimap.rect.top - draggerHeight / 2;
var finalX = x - Minimap.rect.left - draggerWidth / 2;
var maxValidY = (Minimap.workspace.getMetrics().contentHeight - Minimap.workspace.getMetrics().viewHeight)
* Minimap.minimap.scale;
var maxValidX = (Minimap.workspace.getMetrics().contentWidth - Minimap.workspace.getMetrics().viewWidth)
* Minimap.minimap.scale;
if(y + draggerHeight / 2 > Minimap.rect.bottom){
finalY = Minimap.rect.bottom - Minimap.rect.top - draggerHeight;
}else if(y < Minimap.rect.top + draggerHeight / 2){
finalY = 0;
}
if(x + draggerWidth / 2 > Minimap.rect.right){
finalX = Minimap.rect.right - Minimap.rect.left - draggerWidth;
}else if(x < Minimap.rect.left + draggerWidth / 2){
finalX = 0;
}
// Do not go below lower bound of scrollbar.
if(finalY > maxValidY){
finalY = maxValidY;
}
if(finalX > maxValidX){
finalX = maxValidX;
}
Minimap.mapDragger.setAttribute("y", finalY);
Minimap.mapDragger.setAttribute("x", finalX);
// Required, otherwise creates a feedback loop.
Minimap.disableScrollChange = true;
Minimap.workspace.scrollbar.vScroll.set((finalY * Minimap.workspace.scale) / Minimap.minimap.scale);
Minimap.workspace.scrollbar.hScroll.set((finalX * Minimap.workspace.scale) / Minimap.minimap.scale);
Minimap.disableScrollChange = false;
};
/**
* Handles the onclick event on the minimapBoundingBox, paramaters are passed by
* the event handler.
* @param {Float} position This is the absolute postion of the scrollbar.
* @param {boolean} horizontal Informs if the change event if for horizontal(true)
* scrollbar or vertical(false) scrollbar.
*/
Minimap.onScrollChange = function(position, horizontal){
if(Minimap.disableScrollChange){
return;
}
var newDraggerPosition = (position * Minimap.minimap.scale / Minimap.workspace.scale);
if(horizontal){
// Change the horizontal position of dragger.
Minimap.mapDragger.setAttribute("x", newDraggerPosition);
}
else{
// Change the vertical position of dragger.
Minimap.mapDragger.setAttribute("y", newDraggerPosition);
}
};