mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-06-03 00:54:57 -04:00
Replace playground with benchmark
This commit is contained in:
parent
921ce24195
commit
e8fd5b1f13
7 changed files with 76 additions and 698 deletions
|
@ -1,81 +0,0 @@
|
|||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Scratch VM Benchmark</title>
|
||||
<link rel="stylesheet" href="./benchmark.css" type="text/css" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<h2>Scratch VM Benchmark</h2>
|
||||
<p>
|
||||
Welcome to the scratch-vm benchmark. This tool helps you profile a scratch
|
||||
project. When you load the page, it:
|
||||
<ol>
|
||||
<li>loads the default project
|
||||
<li>runs the project for 4 seconds to warm up
|
||||
<li>profiles for 6 seconds
|
||||
<li>stops and reports
|
||||
</ol>
|
||||
</p>
|
||||
<input type="text" value="119615668">
|
||||
<button class="run">run</button>
|
||||
<p>
|
||||
<i>Try a different project, like `130041250`</i>
|
||||
</p>
|
||||
|
||||
<canvas id="scratch-stage"></canvas><br />
|
||||
|
||||
<div class="loading">
|
||||
<label>Loading:</label>
|
||||
<span class="loading-complete">0</span> / <span class="loading-total">0</span>
|
||||
</div>
|
||||
<div class="profile-count-group">
|
||||
<div class="profile-count">
|
||||
<label>Percent of time worked:</label>
|
||||
<span class="profile-count-value profile-count-amount-recorded">...</span>
|
||||
</div>
|
||||
<div class="profile-count">
|
||||
<label>Steps looped:</label>
|
||||
<span class="profile-count-value profile-count-steps-looped">...</span>
|
||||
</div>
|
||||
<div class="profile-count">
|
||||
<label>Blocks executed:</label>
|
||||
<span class="profile-count-value profile-count-blocks-executed">...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-tables">
|
||||
<table class="profile-count-frame-table" cellspacing="0">
|
||||
<thead>
|
||||
<tr class="profile-count-frame-head"><th>Frame</th><th>Self Time</th><th>Total Time</th><th>Executions</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="profiler-count-running"><td colspan="4"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="profile-count-opcode-table" cellspacing="0">
|
||||
<thead>
|
||||
<tr class="profile-count-opcode-head">
|
||||
<th>opcode</th><th>Self Time</th><th>Total Time</th><th>Executions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="profiler-count-running"><td colspan="4"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="blocks"></div>
|
||||
|
||||
<!-- FPS counter, Syntax highlighter, Blocks, Renderer -->
|
||||
<script src="../playground/vendor.js"></script>
|
||||
<!-- Storage module -->
|
||||
<script src="../playground/scratch-storage.js"></script>
|
||||
<!-- VM -->
|
||||
<script src="../playground/scratch-vm.js"></script>
|
||||
<!-- Playground -->
|
||||
<script src="./benchmark.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -3,88 +3,79 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Scratch VM Playground</title>
|
||||
<link rel="stylesheet" href="playground.css">
|
||||
<link rel="stylesheet" href="zenburn.css">
|
||||
<title>Scratch VM Benchmark</title>
|
||||
<link rel="stylesheet" href="./benchmark.css" type="text/css" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div id="vm-devtools">
|
||||
<h2>Scratch VM Playground</h2>
|
||||
<select id="selectedTarget" multiple></select>
|
||||
<div id="projectButtons">
|
||||
<button id="greenflag">Green flag</button>
|
||||
<button id="stopall">Stop</button>
|
||||
</div>
|
||||
<div>
|
||||
Turbo: <input id='turbomode' type='checkbox' />
|
||||
</div>
|
||||
<div>
|
||||
Compatibility (30 TPS): <input id='compatmode' type='checkbox' />
|
||||
</div>
|
||||
<br />
|
||||
<ul id="playgroundLinks">
|
||||
<li><a id="renderexplorer-link" href="#">Renderer</a></li>
|
||||
<li><a id="threadexplorer-link" href="#">Threads</a></li>
|
||||
<li><a id="blockexplorer-link" href="#">Block Representation</a></li>
|
||||
<li><a id="importexport-link" href="#">Import/Export</a></li>
|
||||
</ul><br />
|
||||
<div id="tab-renderexplorer">
|
||||
Renderer<br />
|
||||
<canvas id="scratch-stage" style="width: 480px; height: 360px;"></canvas><br />
|
||||
x: <input id='sinfo-x' />
|
||||
y: <input id='sinfo-y' /><br />
|
||||
size: <input id='sinfo-size' /><br />
|
||||
dir: <input id='sinfo-direction' />
|
||||
rotation style: <input id='sinfo-rotationstyle' /><br />
|
||||
visible: <input id='sinfo-visible' />
|
||||
<button id='sinfo-post'>Post</button>
|
||||
</div>
|
||||
<div id="tab-threadexplorer">
|
||||
Thread explorer
|
||||
<pre id="threadexplorer"></pre>
|
||||
</div>
|
||||
<div id="tab-blockexplorer">
|
||||
Block explorer
|
||||
<pre id="blockexplorer"></pre>
|
||||
</div>
|
||||
<div id="tab-importexport">
|
||||
Import/Export<br />
|
||||
Project ID: <input id="projectId" value="119615668" />
|
||||
<button id="projectLoadButton">Load</button>
|
||||
<button id="createEmptyProject">New Project</button><br />
|
||||
<p>
|
||||
<input type="button" value="Export to JSON" onclick="toJson()">
|
||||
|
||||
<input type="button" value="Import from JSON" onclick="fromJson()" id="import">
|
||||
<br /><br />
|
||||
<textarea id="importExport"></textarea>
|
||||
</p>
|
||||
</div>
|
||||
<h2>Scratch VM Benchmark</h2>
|
||||
<p>
|
||||
Welcome to the scratch-vm benchmark. This tool helps you profile a scratch
|
||||
project. When you load the page, it:
|
||||
<ol>
|
||||
<li>loads the default project and enables turbo mode
|
||||
<li>runs the project for 4 seconds to warm up
|
||||
<li>profiles for 6 seconds
|
||||
<li>stops and reports
|
||||
</ol>
|
||||
</p>
|
||||
<input type="text" value="119615668">
|
||||
<button class="run">run</button>
|
||||
<p>
|
||||
<i>Try a different project, like `130041250`</i>
|
||||
</p>
|
||||
|
||||
<canvas id="scratch-stage"></canvas><br />
|
||||
|
||||
<div class="loading">
|
||||
<label>Loading:</label>
|
||||
<span class="loading-complete">0</span> / <span class="loading-total">0</span>
|
||||
</div>
|
||||
<div class="profile-count-group">
|
||||
<div class="profile-count">
|
||||
<label>Percent of time worked:</label>
|
||||
<span class="profile-count-value profile-count-amount-recorded">...</span>
|
||||
</div>
|
||||
<div class="profile-count">
|
||||
<label>Steps looped:</label>
|
||||
<span class="profile-count-value profile-count-steps-looped">...</span>
|
||||
</div>
|
||||
<div class="profile-count">
|
||||
<label>Blocks executed:</label>
|
||||
<span class="profile-count-value profile-count-blocks-executed">...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="blocks"></div>
|
||||
<div class="profile-tables">
|
||||
<table class="profile-count-frame-table" cellspacing="0">
|
||||
<thead>
|
||||
<tr class="profile-count-frame-head"><th>Frame</th><th>Self Time</th><th>Total Time</th><th>Executions</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="profiler-count-running"><td colspan="4"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- FPS counter, Syntax highlighter, Blocks, Renderer -->
|
||||
<script src="./vendor.js"></script>
|
||||
<!-- Storage module -->
|
||||
<script src="./scratch-storage.js"></script>
|
||||
<!-- VM -->
|
||||
<script src="./scratch-vm.js"></script>
|
||||
<!-- Playground -->
|
||||
<script src="./playground.js"></script>
|
||||
<script>
|
||||
function toJson() {
|
||||
var output = document.getElementById('importExport');
|
||||
var json = window.Scratch.vm.toJSON();
|
||||
output.value = json;
|
||||
output.focus();
|
||||
output.select();
|
||||
}
|
||||
<table class="profile-count-opcode-table" cellspacing="0">
|
||||
<thead>
|
||||
<tr class="profile-count-opcode-head">
|
||||
<th>opcode</th><th>Self Time</th><th>Total Time</th><th>Executions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="profiler-count-running"><td colspan="4"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
function fromJson() {
|
||||
var input = document.getElementById('importExport');
|
||||
window.Scratch.vm.fromJSON(input.value);
|
||||
}
|
||||
</script>
|
||||
<div id="blocks"></div>
|
||||
|
||||
<!-- FPS counter, Syntax highlighter, Blocks, Renderer -->
|
||||
<script src="./vendor.js"></script>
|
||||
<!-- Storage module -->
|
||||
<script src="./scratch-storage.js"></script>
|
||||
<!-- VM -->
|
||||
<script src="./scratch-vm.js"></script>
|
||||
<!-- Playground -->
|
||||
<script src="./benchmark.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
body {
|
||||
background: rgb(36,36,36);
|
||||
}
|
||||
a {
|
||||
color: rgb(217,217,217);
|
||||
}
|
||||
h2 {
|
||||
font-size: 1em;
|
||||
}
|
||||
#blocks {
|
||||
position: absolute;
|
||||
left: 40%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
font-family: "Helvetica Neue", Helvetica, sans-serif;
|
||||
}
|
||||
#vm-devtools {
|
||||
color: rgb(217,217,217);
|
||||
position: absolute;
|
||||
left: 1%;
|
||||
right: 60%;
|
||||
top: 1%;
|
||||
bottom: 0;
|
||||
width: 35%;
|
||||
}
|
||||
#blockexplorer, #threadexplorer, #importexport {
|
||||
position: absolute;
|
||||
height: 75%;
|
||||
overflow: scroll;
|
||||
border: 1px solid #fff;
|
||||
background: rgb(36,36,36);
|
||||
color: rgb(217,217,217);
|
||||
font-family: monospace;
|
||||
font-size: 10pt;
|
||||
width: 480px;
|
||||
height: 360px;
|
||||
}
|
||||
#tab-blockexplorer, #tab-threadexplorer, #tab-importexport {
|
||||
display: none;
|
||||
}
|
||||
#importExport {
|
||||
width: 480px;
|
||||
height: 360px;
|
||||
background: rgb(36,36,36);
|
||||
color: rgb(217,217,217);
|
||||
}
|
||||
#projectId {
|
||||
background: rgb(36,36,36);
|
||||
color: rgb(217,217,217);
|
||||
font-family: monospace;
|
||||
font-size: 10pt;
|
||||
}
|
||||
ul#playgroundLinks {
|
||||
display: block;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background-color: #333;
|
||||
}
|
||||
#playgroundLinks li {
|
||||
float: left;
|
||||
}
|
||||
#playgroundLinks li a {
|
||||
display: block;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#playgroundLinks li a:hover {
|
||||
background-color: #111;
|
||||
}
|
|
@ -1,402 +0,0 @@
|
|||
const Scratch = window.Scratch = window.Scratch || {};
|
||||
|
||||
const ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/';
|
||||
const PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/';
|
||||
|
||||
const loadProject = function () {
|
||||
let id = location.hash.substring(1);
|
||||
if (id.length < 1 || !isFinite(id)) {
|
||||
id = '119615668';
|
||||
}
|
||||
Scratch.vm.downloadProjectId(id);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Asset} asset - calculate a URL for this asset.
|
||||
* @returns {string} a URL to download a project file.
|
||||
*/
|
||||
const getProjectUrl = function (asset) {
|
||||
const assetIdParts = asset.assetId.split('.');
|
||||
const assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/'];
|
||||
if (assetIdParts[1]) {
|
||||
assetUrlParts.push(assetIdParts[1]);
|
||||
}
|
||||
return assetUrlParts.join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Asset} asset - calculate a URL for this asset.
|
||||
* @returns {string} a URL to download a project asset (PNG, WAV, etc.)
|
||||
*/
|
||||
const getAssetUrl = function (asset) {
|
||||
const assetUrlParts = [
|
||||
ASSET_SERVER,
|
||||
'internalapi/asset/',
|
||||
asset.assetId,
|
||||
'.',
|
||||
asset.dataFormat,
|
||||
'/get/'
|
||||
];
|
||||
return assetUrlParts.join('');
|
||||
};
|
||||
|
||||
const addProfilerPanels = function (
|
||||
stats, vm, panelName = 'BLK%', panelFg = '#fff', panelBg = '#111') {
|
||||
|
||||
vm.runtime.enableProfiling();
|
||||
if (vm.runtime.profiler === null) {
|
||||
// Profiler isn't available on the local system.
|
||||
return;
|
||||
}
|
||||
|
||||
const blockPercentPanel = stats.addPanel(new window.Stats.Panel(panelName, panelFg, panelBg));
|
||||
|
||||
// Store the profiler reference for later.
|
||||
const profiler = vm.runtime.profiler;
|
||||
// Set profiler to null to disable profiling until later.
|
||||
vm.runtime.profiler = null;
|
||||
|
||||
const stepThreadsProfilerId = profiler.idByName('Sequencer.stepThreads');
|
||||
const blockFunctionProfilerId = profiler.idByName('blockFunction');
|
||||
|
||||
let blockFunctionTime = 0;
|
||||
const stepValues = [];
|
||||
let lastUpdate = Date.now();
|
||||
|
||||
// Collect time used by `blockFunction` calls in `execute` and add a column
|
||||
// to the stats graph of the average for the last second of recordings.
|
||||
profiler.onFrame = function ({id, totalTime}) {
|
||||
if (id === stepThreadsProfilerId && totalTime > 0) {
|
||||
// This frame wraps Sequencer.stepThreads.
|
||||
|
||||
// Push the most recently summed blockFunctionTime.
|
||||
stepValues.push(blockFunctionTime / totalTime * 100);
|
||||
// Every second, average the pushed values and render that as a new
|
||||
// column in the stats graph.
|
||||
if (Date.now() - lastUpdate > 1000) {
|
||||
lastUpdate = Date.now();
|
||||
const average = stepValues.reduce(
|
||||
(a, b) => a + b,
|
||||
0) / stepValues.length;
|
||||
blockPercentPanel.update(average, 100);
|
||||
stepValues.length = 0;
|
||||
}
|
||||
blockFunctionTime = 0;
|
||||
} else if (id === blockFunctionProfilerId) {
|
||||
// This frame wraps around each blockFunction call.
|
||||
blockFunctionTime += totalTime;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the stats panel to not display by default.
|
||||
blockPercentPanel.dom.style.display = 'none';
|
||||
|
||||
// When the parent of the stats graphs is clicked, check if the
|
||||
// blockPercentPanel is visible. If it is visible, enable profiling by
|
||||
// setting the runtime's profiler to the stored Profiler instance. If it is
|
||||
// not visible, disable profiling by setting the profiler to null.
|
||||
stats.dom.addEventListener('click', () => {
|
||||
if (blockPercentPanel.dom.style.display === 'block') {
|
||||
vm.runtime.profiler = profiler;
|
||||
} else {
|
||||
vm.runtime.profiler = null;
|
||||
}
|
||||
});
|
||||
|
||||
return blockPercentPanel;
|
||||
};
|
||||
|
||||
window.onload = function () {
|
||||
// Lots of global variables to make debugging easier
|
||||
// Instantiate the VM.
|
||||
const vm = new window.VirtualMachine();
|
||||
Scratch.vm = vm;
|
||||
|
||||
const storage = new ScratchStorage(); /* global ScratchStorage */
|
||||
const AssetType = storage.AssetType;
|
||||
storage.addWebSource([AssetType.Project], getProjectUrl);
|
||||
storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl);
|
||||
vm.attachStorage(storage);
|
||||
|
||||
// Loading projects from the server.
|
||||
document.getElementById('projectLoadButton').onclick = function () {
|
||||
document.location = `#${document.getElementById('projectId').value}`;
|
||||
location.reload();
|
||||
};
|
||||
loadProject();
|
||||
|
||||
// Instantiate the renderer and connect it to the VM.
|
||||
const canvas = document.getElementById('scratch-stage');
|
||||
const renderer = new window.RenderWebGL(canvas);
|
||||
Scratch.renderer = renderer;
|
||||
vm.attachRenderer(renderer);
|
||||
const audioEngine = new window.AudioEngine();
|
||||
vm.attachAudioEngine(audioEngine);
|
||||
|
||||
// Instantiate scratch-blocks and attach it to the DOM.
|
||||
const workspace = window.Blockly.inject('blocks', {
|
||||
media: './media/',
|
||||
zoom: {
|
||||
controls: true,
|
||||
wheel: true,
|
||||
startScale: 0.75
|
||||
},
|
||||
colours: {
|
||||
workspace: '#334771',
|
||||
flyout: '#283856',
|
||||
scrollbar: '#24324D',
|
||||
scrollbarHover: '#0C111A',
|
||||
insertionMarker: '#FFFFFF',
|
||||
insertionMarkerOpacity: 0.3,
|
||||
fieldShadow: 'rgba(255, 255, 255, 0.3)',
|
||||
dragShadowOpacity: 0.6
|
||||
}
|
||||
});
|
||||
Scratch.workspace = workspace;
|
||||
|
||||
// Attach scratch-blocks events to VM.
|
||||
workspace.addChangeListener(vm.blockListener);
|
||||
workspace.addChangeListener(vm.variableListener);
|
||||
const flyoutWorkspace = workspace.getFlyout().getWorkspace();
|
||||
flyoutWorkspace.addChangeListener(vm.flyoutBlockListener);
|
||||
flyoutWorkspace.addChangeListener(vm.monitorBlockListener);
|
||||
|
||||
// Create FPS counter.
|
||||
const stats = new window.Stats();
|
||||
document.getElementById('tab-renderexplorer').appendChild(stats.dom);
|
||||
stats.dom.style.position = 'relative';
|
||||
addProfilerPanels(stats, vm, 'BLK%', '#fff', '#111');
|
||||
stats.begin();
|
||||
|
||||
// Playground data tabs.
|
||||
// Block representation tab.
|
||||
const blockexplorer = document.getElementById('blockexplorer');
|
||||
const updateBlockExplorer = function (blocks) {
|
||||
blockexplorer.innerHTML = JSON.stringify(blocks, null, 2);
|
||||
window.hljs.highlightBlock(blockexplorer);
|
||||
};
|
||||
|
||||
// Thread representation tab.
|
||||
const threadexplorer = document.getElementById('threadexplorer');
|
||||
let cachedThreadJSON = '';
|
||||
const updateThreadExplorer = function (newJSON) {
|
||||
if (newJSON !== cachedThreadJSON) {
|
||||
cachedThreadJSON = newJSON;
|
||||
threadexplorer.innerHTML = cachedThreadJSON;
|
||||
window.hljs.highlightBlock(threadexplorer);
|
||||
}
|
||||
};
|
||||
|
||||
// Only request data from the VM thread if the appropriate tab is open.
|
||||
Scratch.exploreTabOpen = false;
|
||||
const getPlaygroundData = function () {
|
||||
vm.getPlaygroundData();
|
||||
if (Scratch.exploreTabOpen) {
|
||||
window.requestAnimationFrame(getPlaygroundData);
|
||||
}
|
||||
};
|
||||
|
||||
// VM handlers.
|
||||
// Receipt of new playground data (thread, block representations).
|
||||
vm.on('playgroundData', data => {
|
||||
updateThreadExplorer(data.threads);
|
||||
updateBlockExplorer(data.blocks);
|
||||
});
|
||||
|
||||
// Receipt of new block XML for the selected target.
|
||||
vm.on('workspaceUpdate', data => {
|
||||
workspace.clear();
|
||||
const dom = window.Blockly.Xml.textToDom(data.xml);
|
||||
window.Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
});
|
||||
|
||||
// Receipt of new list of targets, selected target update.
|
||||
const selectedTarget = document.getElementById('selectedTarget');
|
||||
vm.on('targetsUpdate', data => {
|
||||
// Clear select box.
|
||||
while (selectedTarget.firstChild) {
|
||||
selectedTarget.removeChild(selectedTarget.firstChild);
|
||||
}
|
||||
// Generate new select box.
|
||||
for (let i = 0; i < data.targetList.length; i++) {
|
||||
const targetOption = document.createElement('option');
|
||||
targetOption.setAttribute('value', data.targetList[i].id);
|
||||
// If target id matches editingTarget id, select it.
|
||||
if (data.targetList[i].id === data.editingTarget) {
|
||||
targetOption.setAttribute('selected', 'selected');
|
||||
}
|
||||
targetOption.appendChild(
|
||||
document.createTextNode(data.targetList[i].name)
|
||||
);
|
||||
selectedTarget.appendChild(targetOption);
|
||||
}
|
||||
});
|
||||
selectedTarget.onchange = function () {
|
||||
vm.setEditingTarget(this.value);
|
||||
};
|
||||
|
||||
// Feedback for stacks and blocks running.
|
||||
vm.on('SCRIPT_GLOW_ON', data => {
|
||||
workspace.glowStack(data.id, true);
|
||||
});
|
||||
vm.on('SCRIPT_GLOW_OFF', data => {
|
||||
workspace.glowStack(data.id, false);
|
||||
});
|
||||
vm.on('BLOCK_GLOW_ON', data => {
|
||||
workspace.glowBlock(data.id, true);
|
||||
});
|
||||
vm.on('BLOCK_GLOW_OFF', data => {
|
||||
workspace.glowBlock(data.id, false);
|
||||
});
|
||||
vm.on('VISUAL_REPORT', data => {
|
||||
workspace.reportValue(data.id, data.value);
|
||||
});
|
||||
|
||||
vm.on('SPRITE_INFO_REPORT', data => {
|
||||
if (data.id !== selectedTarget.value) return; // Not the editingTarget
|
||||
document.getElementById('sinfo-x').value = data.x;
|
||||
document.getElementById('sinfo-y').value = data.y;
|
||||
document.getElementById('sinfo-size').value = data.size;
|
||||
document.getElementById('sinfo-direction').value = data.direction;
|
||||
document.getElementById('sinfo-rotationstyle').value = data.rotationStyle;
|
||||
document.getElementById('sinfo-visible').value = data.visible;
|
||||
});
|
||||
|
||||
document.getElementById('sinfo-post').addEventListener('click', () => {
|
||||
const data = {};
|
||||
data.x = document.getElementById('sinfo-x').value;
|
||||
data.y = document.getElementById('sinfo-y').value;
|
||||
data.direction = document.getElementById('sinfo-direction').value;
|
||||
data.rotationStyle = document.getElementById('sinfo-rotationstyle').value;
|
||||
data.visible = document.getElementById('sinfo-visible').value === 'true';
|
||||
vm.postSpriteInfo(data);
|
||||
});
|
||||
|
||||
// Feed mouse events as VM I/O events.
|
||||
document.addEventListener('mousemove', e => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const coordinates = {
|
||||
x: e.clientX - rect.left,
|
||||
y: e.clientY - rect.top,
|
||||
canvasWidth: rect.width,
|
||||
canvasHeight: rect.height
|
||||
};
|
||||
Scratch.vm.postIOData('mouse', coordinates);
|
||||
});
|
||||
canvas.addEventListener('mousedown', e => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const data = {
|
||||
isDown: true,
|
||||
x: e.clientX - rect.left,
|
||||
y: e.clientY - rect.top,
|
||||
canvasWidth: rect.width,
|
||||
canvasHeight: rect.height
|
||||
};
|
||||
Scratch.vm.postIOData('mouse', data);
|
||||
e.preventDefault();
|
||||
});
|
||||
canvas.addEventListener('mouseup', e => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const data = {
|
||||
isDown: false,
|
||||
x: e.clientX - rect.left,
|
||||
y: e.clientY - rect.top,
|
||||
canvasWidth: rect.width,
|
||||
canvasHeight: rect.height
|
||||
};
|
||||
Scratch.vm.postIOData('mouse', data);
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// Feed keyboard events as VM I/O events.
|
||||
document.addEventListener('keydown', e => {
|
||||
// Don't capture keys intended for Blockly inputs.
|
||||
if (e.target !== document && e.target !== document.body) {
|
||||
return;
|
||||
}
|
||||
Scratch.vm.postIOData('keyboard', {
|
||||
keyCode: e.keyCode,
|
||||
isDown: true
|
||||
});
|
||||
e.preventDefault();
|
||||
});
|
||||
document.addEventListener('keyup', e => {
|
||||
// Always capture up events,
|
||||
// even those that have switched to other targets.
|
||||
Scratch.vm.postIOData('keyboard', {
|
||||
keyCode: e.keyCode,
|
||||
isDown: false
|
||||
});
|
||||
// E.g., prevent scroll.
|
||||
if (e.target !== document && e.target !== document.body) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Run threads
|
||||
vm.start();
|
||||
|
||||
// Inform VM of animation frames.
|
||||
const animate = function () {
|
||||
stats.update();
|
||||
requestAnimationFrame(animate);
|
||||
};
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
// Handlers for green flag and stop all.
|
||||
document.getElementById('greenflag').addEventListener('click', () => {
|
||||
vm.greenFlag();
|
||||
});
|
||||
document.getElementById('stopall').addEventListener('click', () => {
|
||||
vm.stopAll();
|
||||
});
|
||||
document.getElementById('turbomode').addEventListener('change', () => {
|
||||
const turboOn = document.getElementById('turbomode').checked;
|
||||
vm.setTurboMode(turboOn);
|
||||
});
|
||||
document.getElementById('compatmode').addEventListener('change',
|
||||
() => {
|
||||
const compatibilityMode = document.getElementById('compatmode').checked;
|
||||
vm.setCompatibilityMode(compatibilityMode);
|
||||
});
|
||||
const tabBlockExplorer = document.getElementById('tab-blockexplorer');
|
||||
const tabThreadExplorer = document.getElementById('tab-threadexplorer');
|
||||
const tabRenderExplorer = document.getElementById('tab-renderexplorer');
|
||||
const tabImportExport = document.getElementById('tab-importexport');
|
||||
|
||||
// Handlers to show different explorers.
|
||||
document.getElementById('threadexplorer-link').addEventListener('click',
|
||||
() => {
|
||||
Scratch.exploreTabOpen = true;
|
||||
getPlaygroundData();
|
||||
tabBlockExplorer.style.display = 'none';
|
||||
tabRenderExplorer.style.display = 'none';
|
||||
tabThreadExplorer.style.display = 'block';
|
||||
tabImportExport.style.display = 'none';
|
||||
});
|
||||
document.getElementById('blockexplorer-link').addEventListener('click',
|
||||
() => {
|
||||
Scratch.exploreTabOpen = true;
|
||||
getPlaygroundData();
|
||||
tabBlockExplorer.style.display = 'block';
|
||||
tabRenderExplorer.style.display = 'none';
|
||||
tabThreadExplorer.style.display = 'none';
|
||||
tabImportExport.style.display = 'none';
|
||||
});
|
||||
document.getElementById('renderexplorer-link').addEventListener('click',
|
||||
() => {
|
||||
Scratch.exploreTabOpen = false;
|
||||
tabBlockExplorer.style.display = 'none';
|
||||
tabRenderExplorer.style.display = 'block';
|
||||
tabThreadExplorer.style.display = 'none';
|
||||
tabImportExport.style.display = 'none';
|
||||
});
|
||||
document.getElementById('importexport-link').addEventListener('click',
|
||||
() => {
|
||||
Scratch.exploreTabOpen = false;
|
||||
tabBlockExplorer.style.display = 'none';
|
||||
tabRenderExplorer.style.display = 'none';
|
||||
tabThreadExplorer.style.display = 'none';
|
||||
tabImportExport.style.display = 'block';
|
||||
});
|
||||
};
|
|
@ -84,75 +84,14 @@ module.exports = [
|
|||
'scratch-blocks/dist/vertical.js',
|
||||
// Audio
|
||||
'scratch-audio',
|
||||
// Storage
|
||||
'scratch-storage',
|
||||
// Renderer
|
||||
'scratch-render'
|
||||
]
|
||||
},
|
||||
output: {
|
||||
libraryTarget: 'umd',
|
||||
path: path.resolve('playground')
|
||||
},
|
||||
module: {
|
||||
rules: base.module.rules.concat([
|
||||
{
|
||||
test: require.resolve('./src/index.js'),
|
||||
loader: 'expose-loader?VirtualMachine'
|
||||
},
|
||||
{
|
||||
test: require.resolve('stats.js/build/stats.min.js'),
|
||||
loader: 'script-loader'
|
||||
},
|
||||
{
|
||||
test: require.resolve('highlightjs/highlight.pack.min.js'),
|
||||
loader: 'script-loader'
|
||||
},
|
||||
{
|
||||
test: require.resolve('scratch-blocks/dist/vertical.js'),
|
||||
loader: 'expose-loader?Blockly'
|
||||
},
|
||||
{
|
||||
test: require.resolve('scratch-audio'),
|
||||
loader: 'expose-loader?AudioEngine'
|
||||
},
|
||||
{
|
||||
test: require.resolve('scratch-render'),
|
||||
loader: 'expose-loader?RenderWebGL'
|
||||
}
|
||||
])
|
||||
},
|
||||
plugins: base.plugins.concat([
|
||||
new CopyWebpackPlugin([{
|
||||
from: 'node_modules/scratch-blocks/media',
|
||||
to: 'media'
|
||||
}, {
|
||||
from: 'node_modules/highlightjs/styles/zenburn.css'
|
||||
}, {
|
||||
from: 'node_modules/scratch-storage/dist/web'
|
||||
}, {
|
||||
from: 'src/playground'
|
||||
}])
|
||||
])
|
||||
}),
|
||||
// Benchmark
|
||||
defaultsDeep({}, base, {
|
||||
target: 'web',
|
||||
entry: {
|
||||
'scratch-vm': './src/index.js',
|
||||
'vendor': [
|
||||
// FPS counter
|
||||
'stats.js/build/stats.min.js',
|
||||
// Syntax highlighter
|
||||
'highlightjs/highlight.pack.min.js',
|
||||
// Scratch Blocks
|
||||
'scratch-blocks/dist/vertical.js',
|
||||
// Audio
|
||||
'scratch-audio',
|
||||
// Renderer
|
||||
'scratch-render'
|
||||
]
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'benchmark'),
|
||||
path: path.resolve(__dirname, 'playground'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
|
@ -177,6 +116,10 @@ module.exports = [
|
|||
test: require.resolve('scratch-audio'),
|
||||
loader: 'expose-loader?AudioEngine'
|
||||
},
|
||||
{
|
||||
test: require.resolve('scratch-storage'),
|
||||
loader: 'expose-loader?ScratchStorage'
|
||||
},
|
||||
{
|
||||
test: require.resolve('scratch-render'),
|
||||
loader: 'expose-loader?RenderWebGL'
|
||||
|
@ -192,7 +135,7 @@ module.exports = [
|
|||
}, {
|
||||
from: 'node_modules/scratch-storage/dist/web'
|
||||
}, {
|
||||
from: 'src/benchmark'
|
||||
from: 'src/playground'
|
||||
}])
|
||||
])
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue