Procházet zdrojové kódy

[webgl] Refactored WebGL demos. They reuse a preallocated set of canvases to limit the number of WebGL contexts required. At most 3 canvases can be visible at a time, so we preallocate 3 canvases, then dynamically attach them to placeholder divs based on their visibility. I'm a juggalo.

badlogic před 8 roky
rodič
revize
4fe53ac856

+ 2 - 4
spine-ts/webgl/demos/clipping.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 <div id="clipping-playbutton"></div>
 <div id="clipping-timeline" class="slider"></div>
 <input id="clipping-drawtriangles" type="checkbox"></input> Draw triangles
@@ -17,9 +17,7 @@
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-clippingDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(clippingDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 26 - 35
spine-ts/webgl/demos/clipping.js

@@ -1,7 +1,7 @@
-var clippingDemo = function(canvas, loadingComplete, bgColor) {
+var clippingDemo = function(canvas, bgColor) {
 	var gl, renderer, assetManager;
 	var skeleton, state, bounds;
-	var timeKeeper, loadingScreen;
+	var timeKeeper;
 	var playButton, timeline, isPlaying = true, playTime = 0;
 
 	var DEMO_NAME = "ClippingDemo";
@@ -18,41 +18,31 @@ var clippingDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadText(DEMO_NAME, "atlas1.atlas");
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas1.atlas"), function(path) {
-				return assetManager.get(DEMO_NAME, path);
-			});
-			var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
-			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json")["spineboy"]);
-			skeleton = new spine.Skeleton(skeletonData);
-			state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
-			state.setAnimation(0, "portal", true);
-			state.apply(skeleton);
-			skeleton.updateWorldTransform();
-			var offset = new spine.Vector2();
-			bounds = new spine.Vector2();
-			skeleton.getBounds(offset, bounds, []);
-
-			renderer.camera.position.x = offset.x + bounds.x + 200;
-			renderer.camera.position.y = offset.y + bounds.y / 2 + 100;
-
-			renderer.skeletonDebugRenderer.drawMeshHull = false;
-			renderer.skeletonDebugRenderer.drawMeshTriangles = false;
+	function loadingComplete () {
+		var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas1.atlas"), function(path) {
+			return assetManager.get(DEMO_NAME, path);
+		});
+		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json")["spineboy"]);
+		skeleton = new spine.Skeleton(skeletonData);
+		state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
+		state.setAnimation(0, "portal", true);
+		state.apply(skeleton);
+		skeleton.updateWorldTransform();
+		var offset = new spine.Vector2();
+		bounds = new spine.Vector2();
+		skeleton.getBounds(offset, bounds, []);
+
+		renderer.camera.position.x = offset.x + bounds.x + 200;
+		renderer.camera.position.y = offset.y + bounds.y / 2 + 100;
 
-			setupUI();
+		renderer.skeletonDebugRenderer.drawMeshHull = false;
+		renderer.skeletonDebugRenderer.drawMeshTriangles = false;
 
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+		setupUI();
 	}
 
 	function setupUI() {
@@ -120,9 +110,10 @@ var clippingDemo = function(canvas, loadingComplete, bgColor) {
 		renderer.drawSkeleton(skeleton, true);
 		renderer.drawSkeletonDebug(skeleton, false, ["root"]);
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
 
+	clippingDemo.loadingComplete = loadingComplete;
+	clippingDemo.render = render;
+	clippingDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 5 - 0
spine-ts/webgl/demos/demos.css

@@ -17,6 +17,11 @@ canvas {
 	height: 480px;
 }
 
+.aspect {
+	width: 640px;
+	height: 480px;
+}
+
 .play {
 	background: black;
 	background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath%20fill%3D%27%23F2F2F2%27%20d%3D%27M30.185%2C15.5L12.556%2C24.8V6.201L30.185%2C15.5z%27%2F%3E%3C%2Fsvg%3E");

+ 11 - 11
spine-ts/webgl/demos/demos.html

@@ -30,7 +30,7 @@
 			<div>
 				<h2 id="Spine-versus-sprites-sheets"><a href="#Spine-versus-sprite-sheets">Spine versus sprite sheets</a></h2>
 				<div>
-					<div><canvas id="spritesheets-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<div class="resize"></div>
 					<div id="spritesheets-overlay" class="overlay-hide">
 						<div class="overlay-label" style="width:50%"><b>Spine</b><br><span class="hide-small">All animations, all frame rates<br></span><b>0.18 MB</b></div>
@@ -66,7 +66,7 @@
 			<div>
 				<h2 id="Spine-with-frame-based-animation"><a href="#Spine-with-frame-based-animation">Spine with frame-based animation</a></h2>
 				<div>
-					<div><canvas id="imagechanges-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<table class="timeline layout"><tr>
 						<td><div id="imagechanges-playbutton" class="pause"></div></td>
 						<td><div class="slider" id="imagechanges-timeline"></div></td>
@@ -93,7 +93,7 @@
 			<div>
 				<h2 id="Transitions-and-layering"><a href="#Transitions-and-layering">Transitions and layering</a></h2>
 				<div>
-					<div><canvas id="transitions-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<div id="transitions-overlay" class="overlay-hide">
 						<div class="overlay-label" style="left:25%;"><b>Smooth</b></div>
 						<div class="overlay-label" style="right:30%;"><b>Abrupt</b></div>
@@ -124,7 +124,7 @@
 			<div>
 				<h2 id="Mesh-deformations"><a href="#Mesh-deformations">Mesh deformation</a></h2>
 				<div>
-					<div><canvas id="meshes-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<table class="timeline layout"><tr>
 						<td><div id="meshes-playbutton" class="pause"></div></td>
 						<td><div class="slider" id="meshes-timeline"></div></td>
@@ -166,7 +166,7 @@
 			<div>
 				<h2 id="Skins"><a href="#Skins">Skins</a></h2>
 				<div>
-					<div><canvas id="skins-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<div class="resize"></div>
 				</div>
 				<div>
@@ -203,7 +203,7 @@
 			<div>
 				<h2 id="Inverse-kinematics"><a href="#Inverse-kinematics">Inverse kinematics</a></h2>
 				<div>
-					<div><canvas id="hoverboard-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<div class="resize"></div>
 				</div>
 					<div>
@@ -241,7 +241,7 @@
 			<div>
 				<h2 id="Path-constraints"><a href="#Path-constraints">Path constraints</a></h2>
 				<div>
-					<div><canvas id="vine-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<table class="timeline layout"><tr>
 						<td><div id="vine-playbutton" class="pause"></div></td>
 						<td><div class="slider" id="vine-timeline"></div></td>
@@ -265,7 +265,7 @@
 			</div>
 			<div>
 				<div>
-					<div><canvas id="stretchyman-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<div class="resize"></div>
 				</div>
 				<div>
@@ -289,7 +289,7 @@
 			<div>
 				<h2 id="Clipping"><a href="#Clipping">Clipping</a></h2>
 				<div>
-					<div><canvas id="clipping-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<table class="timeline layout"><tr>
 						<td><div id="clipping-playbutton" class="pause"></div></td>
 						<td><div class="slider" id="clipping-timeline"></div></td>
@@ -316,7 +316,7 @@
 			<div>
 				<h2 id="Transform-constraints"><a href="#Transform-constraints">Transform constraints</a></h2>
 				<div>
-					<div><canvas id="tank-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<table class="timeline layout"><tr>
 						<td><div id="tank-playbutton" class="pause"></div></td>
 						<td><div class="slider" id="tank-timeline"></div></td>
@@ -340,7 +340,7 @@
 			</div>
 			<div>
 				<div>
-					<div><canvas id="transforms-canvas"></canvas></div>
+					<div class="aspect"></div>
 					<div class="resize"></div>
 				</div>
 				<div>

+ 6 - 2
spine-ts/webgl/demos/demos.js

@@ -4,8 +4,9 @@ $(function () {
 		alert("Error: " + message + "\n" + "URL:" + url + "\nLine: " + lineNo);
 	}
 
+
+	spineDemos.init();
 	spineDemos.assetManager = new spine.SharedAssetManager("assets/");
-	spineDemos.loadSliders();
 
 	var demos = [
 		spritesheetsDemo,
@@ -20,8 +21,11 @@ $(function () {
 		tankDemo,
 		transformsDemo,
 	];
+
+	var placeholders = document.getElementsByClassName("aspect");
+
 	for (var i = 0; i < demos.length; i++)
-		demos[i](spineDemos.setupRendering);
+		spineDemos.addDemo(demos[i], placeholders[i]);
 
 	function resizeSliders () {
 		$(".slider").each(function () {

+ 2 - 4
spine-ts/webgl/demos/hoverboard.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>>
+<div class="aspect"></div>
 <input id="hoverboard-drawbones" type="checkbox"></input> Display Bones<br>
 <input id="hoverboard-aim" type="checkbox"></input> Aim<br>
 <button id="hoverboard-shoot">Shoot</button>
@@ -18,9 +18,7 @@
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-hoverboardDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(hoverboardDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 30 - 39
spine-ts/webgl/demos/hoverboard.js

@@ -1,4 +1,4 @@
-var hoverboardDemo = function(canvas, loadingComplete, bgColor) {
+var hoverboardDemo = function(canvas, bgColor) {
 	var COLOR_INNER = new spine.Color(0.8, 0, 0, 0.5);
 	var COLOR_OUTER = new spine.Color(0.8, 0, 0, 0.8);
 	var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.5);
@@ -29,44 +29,34 @@ var hoverboardDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		input = new spine.webgl.Input(canvas);
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas1.atlas"), function(path) {
-				return assetManager.get(DEMO_NAME, path);
-			});
-			var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
-			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json")["spineboy"]);
-			skeleton = new spine.Skeleton(skeletonData);
-			state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
-			state.setAnimation(0, "hoverboard", true);
-			state.apply(skeleton);
-			skeleton.updateWorldTransform();
-			var offset = new spine.Vector2();
-			bounds = new spine.Vector2();
-			skeleton.getBounds(offset, bounds, []);
-			for (var i = 0; i < controlBones.length; i++)
-				hoverTargets.push(null);
-
-			renderer.camera.position.x = offset.x + bounds.x / 2;
-			renderer.camera.position.y = offset.y + bounds.y / 2;
-
-			renderer.skeletonDebugRenderer.drawMeshHull = false;
-			renderer.skeletonDebugRenderer.drawMeshTriangles = false;
-
-			setupUI();
-			setupInput();
-
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+	function loadingComplete () {
+		var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas1.atlas"), function(path) {
+			return assetManager.get(DEMO_NAME, path);
+		});
+		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json")["spineboy"]);
+		skeleton = new spine.Skeleton(skeletonData);
+		state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
+		state.setAnimation(0, "hoverboard", true);
+		state.apply(skeleton);
+		skeleton.updateWorldTransform();
+		var offset = new spine.Vector2();
+		bounds = new spine.Vector2();
+		skeleton.getBounds(offset, bounds, []);
+		for (var i = 0; i < controlBones.length; i++)
+			hoverTargets.push(null);
+
+		renderer.camera.position.x = offset.x + bounds.x / 2;
+		renderer.camera.position.y = offset.y + bounds.y / 2;
+
+		renderer.skeletonDebugRenderer.drawMeshHull = false;
+		renderer.skeletonDebugRenderer.drawMeshTriangles = false;
+
+		setupUI();
+		setupInput();
 	}
 
 	function setupUI () {
@@ -180,9 +170,10 @@ var hoverboardDemo = function(canvas, loadingComplete, bgColor) {
 		}
 		renderer.end();
 		gl.lineWidth(1);
-
-		loadingScreen.draw(true);
 	}
 
+	hoverboardDemo.loadingComplete = loadingComplete;
+	hoverboardDemo.render = render;
+	hoverboardDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 2 - 4
spine-ts/webgl/demos/imagechanges.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 <div id="imagechanges-timeline" class="slider"></div>
 <input id="imagechanges-playbutton" type="button" value="Pause"></input><br>
 <select id="imagechanges-skeleton" size="2"></select>
@@ -17,9 +17,7 @@
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-imageChangesDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(imageChangesDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 8 - 16
spine-ts/webgl/demos/imagechanges.js

@@ -1,4 +1,4 @@
-var imageChangesDemo = function(canvas, loadingComplete, bgColor) {
+var imageChangesDemo = function(canvas, bgColor) {
 	var OUTLINE_COLOR = new spine.Color(0, 0.8, 0, 1);
 
 	var canvas, gl, renderer, input, assetManager;
@@ -23,21 +23,12 @@ var imageChangesDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadText(DEMO_NAME, "atlas1.atlas");
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			skeletons["Alien"] = loadSkeleton("alien", "death", ["head", "splat-fg", "splat-bg"]);
-			skeletons["Dragon"] = loadSkeleton("dragon", "flying", ["R_wing"])
-			setupUI();
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+	function loadingComplete () {
+		skeletons["Alien"] = loadSkeleton("alien", "death", ["head", "splat-fg", "splat-bg"]);
+		skeletons["Dragon"] = loadSkeleton("dragon", "flying", ["R_wing"])
+		setupUI();
 	}
 
 	function setupUI() {
@@ -197,9 +188,10 @@ var imageChangesDemo = function(canvas, loadingComplete, bgColor) {
 		}
 
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
 
+	imageChangesDemo.loadingComplete = loadingComplete;
+	imageChangesDemo.render = render;
+	imageChangesDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 2 - 4
spine-ts/webgl/demos/meshes.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 <div id="meshes-timeline" class="slider"></div>
 <input id="meshes-playbutton" type="button" value="Pause"></input><br>
 <select id="meshes-skeleton" size="3"></select><br>
@@ -19,9 +19,7 @@
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-meshesDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(meshesDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 9 - 16
spine-ts/webgl/demos/meshes.js

@@ -1,4 +1,4 @@
-var meshesDemo = function(canvas, loadingComplete, bgColor) {
+var meshesDemo = function(canvas, bgColor) {
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, bounds;
 	var timeKeeper, loadingScreen;
@@ -21,22 +21,14 @@ var meshesDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadText(DEMO_NAME, "atlas2.atlas");
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
+	function loadingComplete () {
 		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			skeletons["Orange Girl"] = loadSkeleton("orangegirl", "animation");
-			skeletons["Green Girl"] = loadSkeleton("greengirl", "animation");
-			skeletons["Armor Girl"] = loadSkeleton("armorgirl", "animation");
-			setupUI();
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+		skeletons["Orange Girl"] = loadSkeleton("orangegirl", "animation");
+		skeletons["Green Girl"] = loadSkeleton("greengirl", "animation");
+		skeletons["Armor Girl"] = loadSkeleton("armorgirl", "animation");
+		setupUI();
 	}
 
 	function setupUI() {
@@ -157,9 +149,10 @@ var meshesDemo = function(canvas, loadingComplete, bgColor) {
 		renderer.drawSkeleton(skeleton, true);
 		renderer.drawSkeletonDebug(skeleton);
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
 
+	meshesDemo.loadingComplete = loadingComplete;
+	meshesDemo.render = render;
+	meshesDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 2 - 4
spine-ts/webgl/demos/skins.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 <select id="skins-skin"></select><br>
 <button id="skins-randomizeattachments">Random Attachments</button>
 <button id="skins-swingsword">Swing Sword</button><br>
@@ -18,9 +18,7 @@
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-skinsDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(skinsDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 26 - 34
spine-ts/webgl/demos/skins.js

@@ -1,4 +1,4 @@
-var skinsDemo = function(canvas, loadingComplete, bgColor) {
+var skinsDemo = function(canvas, bgColor) {
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, state, offset, bounds;
 	var timeKeeper, loadingScreen;
@@ -21,39 +21,30 @@ var skinsDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		input = new spine.webgl.Input(canvas);
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "heroes.atlas"), function(path) {
-				return assetManager.get(DEMO_NAME, path);
-			});
-			var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
-			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").heroes);
-			skeleton = new spine.Skeleton(skeletonData);
-			skeleton.setSkinByName("Assassin");
-			var stateData = new spine.AnimationStateData(skeleton.data);
-			stateData.defaultMix = 0.2;
-			stateData.setMix("roll", "run", 0);
-			stateData.setMix("jump", "run2", 0);
-			state = new spine.AnimationState(stateData);
-			setupAnimations(state);
-			state.apply(skeleton);
-			skeleton.updateWorldTransform();
-			offset = new spine.Vector2();
-			bounds = new spine.Vector2();
-			skeleton.getBounds(offset, bounds, []);
-			setupUI();
-			setupInput();
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+	function loadingComplete () {
+		var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "heroes.atlas"), function(path) {
+			return assetManager.get(DEMO_NAME, path);
+		});
+		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").heroes);
+		skeleton = new spine.Skeleton(skeletonData);
+		skeleton.setSkinByName("Assassin");
+		var stateData = new spine.AnimationStateData(skeleton.data);
+		stateData.defaultMix = 0.2;
+		stateData.setMix("roll", "run", 0);
+		stateData.setMix("jump", "run2", 0);
+		state = new spine.AnimationState(stateData);
+		setupAnimations(state);
+		state.apply(skeleton);
+		skeleton.updateWorldTransform();
+		offset = new spine.Vector2();
+		bounds = new spine.Vector2();
+		skeleton.getBounds(offset, bounds, []);
+		setupUI();
+		setupInput();
 	}
 
 	function setupInput (){
@@ -213,9 +204,10 @@ var skinsDemo = function(canvas, loadingComplete, bgColor) {
 		var height = scale * texture.getImage().height;
 		renderer.drawTexture(texture, offset.x + bounds.x + 190, offset.y + bounds.y / 2 - height / 2 - 5, width, height);
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
 
+	skinsDemo.loadingComplete = loadingComplete;
+	skinsDemo.render = render;
+	skinsDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 2 - 4
spine-ts/webgl/demos/spritesheets.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 <button id="spritesheets-roar">Roar</button>
 <button id="spritesheets-jump">Jump</button><br>
 Time multiplier
@@ -18,9 +18,7 @@ Time multiplier
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-spritesheetsDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(spritesheetsDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 37 - 45
spine-ts/webgl/demos/spritesheets.js

@@ -1,4 +1,4 @@
-var spritesheetsDemo = function(canvas, loadingComplete, bgColor) {
+var spritesheetsDemo = function(canvas, bgColor) {
 	var SKELETON_ATLAS_COLOR = new spine.Color(0, 0.8, 0, 0.8);
 	var FRAME_ATLAS_COLOR = new spine.Color(0.8, 0, 0, 0.8);
 
@@ -27,50 +27,41 @@ var spritesheetsDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		timeKeeper = new spine.TimeKeeper();
 		input = new spine.webgl.Input(canvas);
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			skeletonAtlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas1.atlas"), function(path) {
-				return assetManager.get(DEMO_NAME, path);
-			});
-			var atlasLoader = new spine.AtlasAttachmentLoader(skeletonAtlas);
-			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").raptor);
-			skeleton = new spine.Skeleton(skeletonData);
-			var stateData = new spine.AnimationStateData(skeleton.data);
-			stateData.defaultMix = 0.5;
-			stateData.setMix("jump", "walk", 0.3);
-			animationState = new spine.AnimationState(stateData);
-			animationState.setAnimation(0, "walk", true);
-			animationState.apply(skeleton);
-			skeleton.updateWorldTransform();
-			offset = new spine.Vector2();
-			bounds = new spine.Vector2();
-			skeleton.getBounds(offset, bounds, []);
-			skeleton.x -= 60;
-
-			skeletonSeq = new spine.Skeleton(skeletonData);
-			walkAnim = skeletonSeq.data.findAnimation("walk");
-			walkAnim.apply(skeletonSeq, 0, 0, true, null, 1, true, false);
-			skeletonSeq.x += bounds.x + 150;
-
-			viewportWidth = ((700 + bounds.x) - offset.x);
-			viewportHeight = ((0 + bounds.y) - offset.y);
-			resize();
-			setupUI();
-			setupInput();
-
-			$("#spritesheets-overlay").removeClass("overlay-hide");
-			$("#spritesheets-overlay").addClass("overlay");
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+	function loadingComplete () {
+		skeletonAtlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas1.atlas"), function(path) {
+			return assetManager.get(DEMO_NAME, path);
+		});
+		var atlasLoader = new spine.AtlasAttachmentLoader(skeletonAtlas);
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").raptor);
+		skeleton = new spine.Skeleton(skeletonData);
+		var stateData = new spine.AnimationStateData(skeleton.data);
+		stateData.defaultMix = 0.5;
+		stateData.setMix("jump", "walk", 0.3);
+		animationState = new spine.AnimationState(stateData);
+		animationState.setAnimation(0, "walk", true);
+		animationState.apply(skeleton);
+		skeleton.updateWorldTransform();
+		offset = new spine.Vector2();
+		bounds = new spine.Vector2();
+		skeleton.getBounds(offset, bounds, []);
+		skeleton.x -= 60;
+
+		skeletonSeq = new spine.Skeleton(skeletonData);
+		walkAnim = skeletonSeq.data.findAnimation("walk");
+		walkAnim.apply(skeletonSeq, 0, 0, true, null, 1, true, false);
+		skeletonSeq.x += bounds.x + 150;
+
+		viewportWidth = ((700 + bounds.x) - offset.x);
+		viewportHeight = ((0 + bounds.y) - offset.y);
+		resize();
+		setupUI();
+		setupInput();
+
+		$("#spritesheets-overlay").removeClass("overlay-hide");
+		$("#spritesheets-overlay").addClass("overlay");
 	}
 
 	function setupUI () {
@@ -150,9 +141,10 @@ var spritesheetsDemo = function(canvas, loadingComplete, bgColor) {
 		renderer.drawSkeleton(skeleton, true);
 		renderer.drawSkeleton(skeletonSeq, true);
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
 
+	spritesheetsDemo.loadingComplete = loadingComplete;
+	spritesheetsDemo.render = render;
+	spritesheetsDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 2 - 4
spine-ts/webgl/demos/stretchyman.html

@@ -9,15 +9,13 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 <input id="stretchyman-drawbones" type="checkbox"></input> Display bones
 </center>
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-stretchymanDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(stretchymanDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 29 - 38
spine-ts/webgl/demos/stretchyman.js

@@ -1,4 +1,4 @@
-var stretchymanDemo = function(canvas, loadingComplete, bgColor) {
+var stretchymanDemo = function(canvas, bgColor) {
 	var COLOR_INNER = new spine.Color(0.8, 0, 0, 0.5);
 	var COLOR_OUTER = new spine.Color(0.8, 0, 0, 0.8);
 	var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.5);
@@ -37,43 +37,33 @@ var stretchymanDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadText(DEMO_NAME, "atlas2.atlas");
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas2.atlas"), function(path) {
-				return assetManager.get(DEMO_NAME, path);
-			});
-			var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
-			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").stretchyman);
-			skeleton = new spine.Skeleton(skeletonData);
-			skeleton.setToSetupPose();
-			skeleton.updateWorldTransform();
-			var offset = new spine.Vector2();
-			bounds = new spine.Vector2();
-			skeleton.getBounds(offset, bounds, []);
-			for (var i = 0; i < controlBones.length; i++) hoverTargets.push(null);
-			state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
-			state.setAnimation(0, "idle", true);
-
-			renderer.camera.position.x = offset.x + bounds.x / 2;
-			renderer.camera.position.y = offset.y + bounds.y / 2;
-
-			renderer.skeletonDebugRenderer.drawMeshHull = false;
-			renderer.skeletonDebugRenderer.drawMeshTriangles = false;
-
-			setupUI();
-			setupInput();
-
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+	function loadingComplete () {
+		var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas2.atlas"), function(path) {
+			return assetManager.get(DEMO_NAME, path);
+		});
+		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").stretchyman);
+		skeleton = new spine.Skeleton(skeletonData);
+		skeleton.setToSetupPose();
+		skeleton.updateWorldTransform();
+		var offset = new spine.Vector2();
+		bounds = new spine.Vector2();
+		skeleton.getBounds(offset, bounds, []);
+		for (var i = 0; i < controlBones.length; i++) hoverTargets.push(null);
+		state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
+		state.setAnimation(0, "idle", true);
+
+		renderer.camera.position.x = offset.x + bounds.x / 2;
+		renderer.camera.position.y = offset.y + bounds.y / 2;
+
+		renderer.skeletonDebugRenderer.drawMeshHull = false;
+		renderer.skeletonDebugRenderer.drawMeshTriangles = false;
+
+		setupUI();
+		setupInput();
 	}
 
 	function setupUI() {
@@ -189,9 +179,10 @@ var stretchymanDemo = function(canvas, loadingComplete, bgColor) {
 		}
 		renderer.end();
 		gl.lineWidth(1);
-
-		loadingScreen.draw(true);
 	}
 
+	stretchymanDemo.loadingComplete = loadingComplete;
+	stretchymanDemo.render = render;
+	stretchymanDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 2 - 4
spine-ts/webgl/demos/tank.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 <div id="tank-timeline" class="slider"></div>
 <input id="tank-playbutton" type="button" value="Pause"></input><br>
 <input id="tank-drawbones" type="checkbox"></input> Display bones
@@ -17,9 +17,7 @@
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-tankDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(tankDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 29 - 37
spine-ts/webgl/demos/tank.js

@@ -1,7 +1,7 @@
-var tankDemo = function(canvas, loadingComplete, bgColor) {
+var tankDemo = function(canvas, bgColor) {
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, state, offset, bounds;
-	var timeKeeper, loadingScreen;
+	var timeKeeper;
 	var playButton, timeLine, isPlaying = true, playTime = 0;
 
 	var DEMO_NAME = "TankDemo";
@@ -19,41 +19,32 @@ var tankDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadText(DEMO_NAME, "atlas2.atlas");
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas2.atlas"), function(path) {
-				return assetManager.get(DEMO_NAME, path);
-			});
-			var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
-			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").tank);
-			skeleton = new spine.Skeleton(skeletonData);
-			state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
-			state.setAnimation(0, "drive", true);
-			state.apply(skeleton);
-			skeleton.updateWorldTransform();
-			offset = new spine.Vector2();
-			bounds = new spine.Vector2();
-			offset.x = -1204.22;
-			bounds.x = 1914.52;
-			bounds.y = 965.78;
-			// skeleton.getBounds(offset, bounds);
-
-			renderer.skeletonDebugRenderer.drawRegionAttachments = false;
-			renderer.skeletonDebugRenderer.drawMeshHull = false;
-			renderer.skeletonDebugRenderer.drawMeshTriangles = false;
-
-			setupUI();
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+	function loadingComplete () {
+		var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas2.atlas"), function(path) {
+			return assetManager.get(DEMO_NAME, path);
+		});
+		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").tank);
+		skeleton = new spine.Skeleton(skeletonData);
+		state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
+		state.setAnimation(0, "drive", true);
+		state.apply(skeleton);
+		skeleton.updateWorldTransform();
+		offset = new spine.Vector2();
+		bounds = new spine.Vector2();
+		offset.x = -1204.22;
+		bounds.x = 1914.52;
+		bounds.y = 965.78;
+		// skeleton.getBounds(offset, bounds);
+
+		renderer.skeletonDebugRenderer.drawRegionAttachments = false;
+		renderer.skeletonDebugRenderer.drawMeshHull = false;
+		renderer.skeletonDebugRenderer.drawMeshTriangles = false;
+
+		setupUI();
 	}
 
 	function setupUI() {
@@ -121,9 +112,10 @@ var tankDemo = function(canvas, loadingComplete, bgColor) {
 		renderer.drawSkeleton(skeleton, true);
 		renderer.drawSkeletonDebug(skeleton, true);
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
 
+	tankDemo.loadingComplete = loadingComplete;
+	tankDemo.render = render;
+	tankDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 2 - 4
spine-ts/webgl/demos/transforms.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 Rotation offset
 <div id="transforms-rotationoffset" class="slider filled"></div><br>
 Translation mix
@@ -18,9 +18,7 @@ Translation mix
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-transformsDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(transformsDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 32 - 41
spine-ts/webgl/demos/transforms.js

@@ -1,4 +1,4 @@
-var transformsDemo = function(canvas, loadingComplete, bgColor) {
+var transformsDemo = function(canvas, bgColor) {
 	var COLOR_INNER = new spine.Color(0.8, 0, 0, 0.5);
 	var COLOR_OUTER = new spine.Color(0.8, 0, 0, 0.8);
 	var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.5);
@@ -6,7 +6,7 @@ var transformsDemo = function(canvas, loadingComplete, bgColor) {
 
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, state, bounds;
-	var timeKeeper, loadingScreen;
+	var timeKeeper;
 	var rotateHandle;
 	var target = null;
 	var hoverTargets = [null, null, null];
@@ -31,45 +31,35 @@ var transformsDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		input = new spine.webgl.Input(canvas);
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas2.atlas"), function(path) {
-				return assetManager.get(DEMO_NAME, path);
-			});
-			var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
-			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").transforms);
-			skeleton = new spine.Skeleton(skeletonData);
-			skeleton.setToSetupPose();
-			skeleton.updateWorldTransform();
-			var offset = new spine.Vector2();
-			bounds = new spine.Vector2();
-			skeleton.getBounds(offset, bounds, []);
-			state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
-			skeleton.setToSetupPose();
-			skeleton.updateWorldTransform();
-			rotateHandle = skeleton.findBone("rotate-handle");
-
-			renderer.camera.position.x = offset.x + bounds.x / 2;
-			renderer.camera.position.y = offset.y + bounds.y / 2;
-
-			renderer.skeletonDebugRenderer.drawRegionAttachments = false;
-			renderer.skeletonDebugRenderer.drawMeshHull = false;
-			renderer.skeletonDebugRenderer.drawMeshTriangles = false;
-
-			setupUI();
-			setupInput();
-
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+	function loadingComplete () {
+		var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas2.atlas"), function(path) {
+			return assetManager.get(DEMO_NAME, path);
+		});
+		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").transforms);
+		skeleton = new spine.Skeleton(skeletonData);
+		skeleton.setToSetupPose();
+		skeleton.updateWorldTransform();
+		var offset = new spine.Vector2();
+		bounds = new spine.Vector2();
+		skeleton.getBounds(offset, bounds, []);
+		state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
+		skeleton.setToSetupPose();
+		skeleton.updateWorldTransform();
+		rotateHandle = skeleton.findBone("rotate-handle");
+
+		renderer.camera.position.x = offset.x + bounds.x / 2;
+		renderer.camera.position.y = offset.y + bounds.y / 2;
+
+		renderer.skeletonDebugRenderer.drawRegionAttachments = false;
+		renderer.skeletonDebugRenderer.drawMeshHull = false;
+		renderer.skeletonDebugRenderer.drawMeshTriangles = false;
+
+		setupUI();
+		setupInput();
 	}
 
 	function setupUI() {
@@ -178,9 +168,10 @@ var transformsDemo = function(canvas, loadingComplete, bgColor) {
 		}
 		gl.lineWidth(1);
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
 
+	transformsDemo.loadingComplete = loadingComplete;
+	transformsDemo.render = render;
+	transformsDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };

+ 2 - 4
spine-ts/webgl/demos/transitions.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 Time multiplier
 <div id="transitions-timeslider" class="slider filled"></div><br>
 <button id="transitions-die">Die</button>
@@ -17,9 +17,7 @@ Time multiplier
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-transitionsDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(transitionsDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 20 - 30
spine-ts/webgl/demos/transitions.js

@@ -5,7 +5,6 @@ var transitionsDemo = function(canvas, loadingComplete, bgColor) {
 	var skeleton, skeletonNoMix, state, stateNoMix, bounds;
 	var timeSlider, timeSliderLabel;
 	var timeKeeper;
-	var loadingScreen;
 
 	var DEMO_NAME = "TransitionsDemo";
 
@@ -29,34 +28,24 @@ var transitionsDemo = function(canvas, loadingComplete, bgColor) {
 
 		input = new spine.webgl.Input(canvas);
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			skeleton = loadSkeleton("spineboy");
-			skeletonNoMix = new spine.Skeleton(skeleton.data);
-			state = createState(0.25);
-			state.multipleMixing = true;
-			setAnimations(state, 0, 0);
-			stateNoMix = createState(0);
-			setAnimations(stateNoMix, -0.25, 0);
-
-			state.apply(skeleton);
-			skeleton.updateWorldTransform();
-			bounds = { offset: new spine.Vector2(), size: new spine.Vector2() };
-			skeleton.getBounds(bounds.offset, bounds.size, []);
-			setupInput();
-			$("#transitions-overlay").removeClass("overlay-hide");
-			$("#transitions-overlay").addClass("overlay");
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+	function loadingComplete () {
+		skeleton = loadSkeleton("spineboy");
+		skeletonNoMix = new spine.Skeleton(skeleton.data);
+		state = createState(0.25);
+		state.multipleMixing = true;
+		setAnimations(state, 0, 0);
+		stateNoMix = createState(0);
+		setAnimations(stateNoMix, -0.25, 0);
+
+		state.apply(skeleton);
+		skeleton.updateWorldTransform();
+		bounds = { offset: new spine.Vector2(), size: new spine.Vector2() };
+		skeleton.getBounds(bounds.offset, bounds.size, []);
+		setupInput();
+		$("#transitions-overlay").removeClass("overlay-hide");
+		$("#transitions-overlay").addClass("overlay");
 	}
 
 	function setupInput() {
@@ -142,9 +131,10 @@ var transitionsDemo = function(canvas, loadingComplete, bgColor) {
 		skeletonNoMix.y = -100;
 		renderer.drawSkeleton(skeletonNoMix, true);
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
+
+	transitionsDemo.loadingComplete = loadingComplete;
+	transitionsDemo.render = render;
+	transitionsDemo.DEMO_NAME = DEMO_NAME;
 	init();
-	return render;
 };

+ 45 - 29
spine-ts/webgl/demos/utils.js

@@ -18,16 +18,36 @@ var spineDemos = {
 		for (var i = 0; i < demos.length; i++) {
 			var demo = demos[i];
 			var canvas = demo.canvas;
-			var renderFunc = demo.renderFunc;
-			if (demo.visible) {
-				if (spineDemos.log) console.log("Rendering " + canvas.id);
-				renderFunc();
+
+			if (!spineDemos.assetManager.isLoadingComplete(demo.DEMO_NAME)) {
+				if (demo.visible) {
+					if (canvas.parentElement != demo.placeholder) {
+						$(canvas).detach();
+						demo.placeholder.appendChild(canvas);
+					}
+					demo.loadingScreen.draw();
+				}
+			} else {
+				if (!demo.loaded) {
+					demo.loadingComplete();
+					demo.loaded = true;
+				}
+
+				if (demo.visible) {
+					if (canvas.parentElement != demo.placeholder) {
+						$(canvas).detach();
+						demo.placeholder.appendChild(canvas);
+					}
+					if (spineDemos.log) console.log("Rendering " + canvas.id);
+					demo.render();
+					demo.loadingScreen.draw(true);
+				}
 			}
 		}
 	}
 
 	function checkElementVisible(demo) {
-		const rect = demo.canvas.getBoundingClientRect();
+		const rect = demo.placeholder.getBoundingClientRect();
 		const windowHeight = (window.innerHeight || document.documentElement.clientHeight);
 		const windowWidth = (window.innerWidth || document.documentElement.clientWidth);
 		const vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
@@ -36,41 +56,37 @@ var spineDemos = {
 		demo.visible = (vertInView && horInView);
 	}
 
-	spineDemos.setupRendering = function (canvas, renderFunc) {
-		var demo = {canvas: canvas, renderFunc: renderFunc, visible: false};
-		$(window).on('DOMContentLoaded load resize scroll', function() {
-			checkElementVisible(demo);
-		});
-		checkElementVisible(demo);
-		if (!spineDemos.loopRunning) {
-			loop();
-			spineDemos.loopRunning = true;
-		}
-		spineDemos.demos.push(demo);
-	};
-
-	spineDemos.init = function () {
-		spineDemos.createCanvases(3);
-		spineDemos.loadSliders();
-	}
-
-	spineDemos.createCanvases = function (numCanvases) {
+	function createCanvases (numCanvases) {
 		for (var i = 0; i < numCanvases; i++) {
 			var canvas = document.createElement("canvas");
 			canvas.ctx = new spine.webgl.ManagedWebGLRenderingContext(canvas, { alpha: false });
+			canvas.id = "canvas-" + i;
 			spineDemos.canvases.push(canvas);
 		}
 	}
 
-	spineDemos.obtainCanvas = function () {
-		return spineDemos.canvases.splice(0, 1)[0];
+	spineDemos.init = function () {
+		createCanvases(3);
+		loadSliders();
+		requestAnimationFrame(loop);
 	}
 
-	spineDemos.freeCanvas = function (canvas) {
-		canvases.push(canvas);
+	spineDemos.addDemo = function (demo, placeholder) {
+		var canvas = spineDemos.canvases[spineDemos.demos.length % spineDemos.canvases.length];
+		demo(canvas);
+		demo.placeholder = placeholder;
+		demo.canvas = canvas;
+		demo.visible = false;
+		var renderer = new spine.webgl.SceneRenderer(canvas, canvas.ctx.gl);
+		demo.loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		$(window).on('DOMContentLoaded load resize scroll', function() {
+			checkElementVisible(demo);
+		});
+		checkElementVisible(demo);
+		spineDemos.demos.push(demo);
 	}
 
-	spineDemos.loadSliders = function () {
+	loadSliders = function () {
 		$(window).resize(function() {
 			$(".slider").each(function () {
 				$(this).data("slider").resized();

+ 2 - 4
spine-ts/webgl/demos/vine.html

@@ -9,7 +9,7 @@
 <body>
 
 <center>
-<div id="canvas"></div>
+<div class="aspect"></div>
 <div id="vine-timeline" class="slider"></div>
 <input id="vine-playbutton" type="button" value="Pause"></input><br>
 <input id="vine-drawbones" type="checkbox"></input> Display bones &amp; path
@@ -17,9 +17,7 @@
 
 <script>
 spineDemos.init();
-var canvas = spineDemos.obtainCanvas();
-document.getElementById("canvas").appendChild(canvas);
-vineDemo(canvas, spineDemos.setupRendering);
+spineDemos.addDemo(vineDemo, document.getElementsByClassName("aspect")[0]);
 </script>
 
 </body>

+ 28 - 37
spine-ts/webgl/demos/vine.js

@@ -1,4 +1,4 @@
-var vineDemo = function(canvas, loadingComplete, bgColor) {
+var vineDemo = function(canvas, bgColor) {
 	var COLOR_INNER = new spine.Color(0.8, 0, 0, 0.5);
 	var COLOR_OUTER = new spine.Color(0.8, 0, 0, 0.8);
 	var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.5);
@@ -6,7 +6,7 @@ var vineDemo = function(canvas, loadingComplete, bgColor) {
 
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, state, bounds;
-	var timeKeeper, loadingScreen;
+	var timeKeeper;
 	var target = null;
 	var hoverTargets = [null, null, null, null, null, null];
 	var controlBones = ["base", "vine-control1", "vine-control2", "vine-control3", "vine-control4"];
@@ -29,44 +29,34 @@ var vineDemo = function(canvas, loadingComplete, bgColor) {
 		assetManager.loadText(DEMO_NAME, "atlas2.atlas");
 		assetManager.loadJson(DEMO_NAME, "demos.json");
 		timeKeeper = new spine.TimeKeeper();
-		loadingScreen = new spine.webgl.LoadingScreen(renderer);
-		requestAnimationFrame(load);
 	}
 
-	function load () {
-		timeKeeper.update();
-		if (assetManager.isLoadingComplete(DEMO_NAME)) {
-			var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas2.atlas"), function(path) {
-				return assetManager.get(DEMO_NAME, path);
-			});
-			var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
-			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").vine);
-			skeleton = new spine.Skeleton(skeletonData);
-			skeleton.setToSetupPose();
-			skeleton.updateWorldTransform();
-			var offset = new spine.Vector2();
-			bounds = new spine.Vector2();
-			skeleton.getBounds(offset, bounds, []);
-			state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
-			state.setAnimation(0, "animation", true);
-			state.apply(skeleton);
-			skeleton.updateWorldTransform();
-
-			renderer.camera.position.x = offset.x + bounds.x / 2;
-			renderer.camera.position.y = offset.y + bounds.y / 2;
+	function loadingComplete () {
+		var atlas = new spine.TextureAtlas(assetManager.get(DEMO_NAME, "atlas2.atlas"), function(path) {
+			return assetManager.get(DEMO_NAME, path);
+		});
+		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(DEMO_NAME, "demos.json").vine);
+		skeleton = new spine.Skeleton(skeletonData);
+		skeleton.setToSetupPose();
+		skeleton.updateWorldTransform();
+		var offset = new spine.Vector2();
+		bounds = new spine.Vector2();
+		skeleton.getBounds(offset, bounds, []);
+		state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
+		state.setAnimation(0, "animation", true);
+		state.apply(skeleton);
+		skeleton.updateWorldTransform();
 
-			renderer.skeletonDebugRenderer.drawMeshHull = false;
-			renderer.skeletonDebugRenderer.drawMeshTriangles = false;
+		renderer.camera.position.x = offset.x + bounds.x / 2;
+		renderer.camera.position.y = offset.y + bounds.y / 2;
 
-			setupUI();
-			setupInput();
+		renderer.skeletonDebugRenderer.drawMeshHull = false;
+		renderer.skeletonDebugRenderer.drawMeshTriangles = false;
 
-			loadingComplete(canvas, render);
-		} else {
-			loadingScreen.draw();
-			requestAnimationFrame(load);
-		}
+		setupUI();
+		setupInput();
 	}
 
 	function setupUI() {
@@ -182,9 +172,10 @@ var vineDemo = function(canvas, loadingComplete, bgColor) {
 		}
 		gl.lineWidth(1);
 		renderer.end();
-
-		loadingScreen.draw(true);
 	}
 
+	vineDemo.loadingComplete = loadingComplete;
+	vineDemo.render = render;
+	vineDemo.DEMO_NAME = DEMO_NAME;
 	init();
 };