Skip to content

Instantly share code, notes, and snippets.

@pbogden
Last active May 2, 2017 13:59
Show Gist options
  • Save pbogden/9e9ce761e43b1d2fdeaf42181468705d to your computer and use it in GitHub Desktop.
Save pbogden/9e9ce761e43b1d2fdeaf42181468705d to your computer and use it in GitHub Desktop.
sprite

Circular sprites

The demo uses:

  • CanvasRenderingContext2D.arc() to create a sprite from a filled disc
  • CanvasRenderingContext2D.createRadialGradient() to create a sprite from a radial gradient
    • Arguments: the coordinates and radii of two circles (start circle & end circle)
    • Returns a CanvasGradient
    • CanvasGradient.addColorStop(stop, color) determines color of gradient
    • "stop" determines where in the gradient (a value between 0 & 1) the "color" begins to apply
  • THREE.Group() -- convenient way to group a collection of objects
  • THREE.SpriteMaterial() -- material used with a sprite
    • THREE.Material() -- provides a renderer-independent description of an object
    • material.color -- optional material color (default is white = 0xffffff)
    • material.map -- optional texture map (default is null)
    • Note: The .map is multiplied by the color
  • THREE.Texture()
    • THREE.Texture(canvas) creates a texture from an HTML canvas
      • remember to set .needsUpdate to "true"
    • THREE.CanvasTexture(canvas) also creates a texture from an HTML canvas
      • API docs: "This is almost the same as the base Texture class, except that it sets needsUpdate to true immediately.
<!DOCTYPE html>
<meta charset='utf-8'>
<title>sprite</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: steelblue;
}
.sprite, .container {
display: inline-block;
}
</style>
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<body>
<div id="WebGL-output" class="container"></div>
<script>
var spriteWidth = spriteHeight = 16 * 16;
// Dimensions of WebGL canvas
var width = 960 - spriteWidth, height = 500;
// Globals used for animation
var requestId, group;
// First slider to select gradient's start radius
var r0 = d3.select('body').append("input")
.attr("type", "range")
.attr('min', 0)
.attr('max', spriteWidth / 2)
.attr('value', 0)
.style('position', "absolute")
.style('top', spriteHeight + 10 + "px")
.style('left', spriteWidth / 4 + "px")
.style('width', spriteWidth / 2 + 'px')
.on('change', changed);
// Second slider to select gradient's end radius
var r1 = d3.select('body')
.append(function() { return r0.node().cloneNode(); })
.attr('value', spriteWidth / 2)
.style('top', spriteHeight + 30 + "px")
.on('change', changed);
// Add a canvas for the sprite
var sprite = d3.select('body').insert(createCanvasDot, "div")
.attr('class', 'sprite')
.style('vertical-align', 'top')
// Add a div container for the WebGL canvas
var container = d3.select("WebGL-output")
.attr('class', "container")
.attr('width', width)
.attr('height', height)
var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera(0, width, 0, height, 0, 1000);
var renderer = new THREE.WebGLRenderer();
// Configure the renderer
renderer.setSize(width, 500)
renderer.setClearColor (0xbbbbbb, 1);
// Position and point the camera toward the center of the scene
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 125;
// Add the renderer to the container
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// Add a group of sprites to the scene
scene.add(createGroupOfSprites());
// Start the animation
render();
// Return a group of sprites
function createGroupOfSprites() {
var material = new THREE.SpriteMaterial();
var n = 10, dx = height / n;
material.map = generateSprite(); // returns THREE.texture()
group = new THREE.Group();
for (var x = 0; x < n; x++) {
for (var y = 0; y < n; y++) {
var sprite = new THREE.Sprite(material);
sprite.scale.set(50, 50, 50); // scale determines size (default: 1 pixel)
sprite.position.set((x + 1 / 2) * dx, (y + 1 / 2) * dx, 0);
group.add(sprite);
}
}
return group
}
// Animate the scene
function render() {
requestId = requestAnimationFrame(render);
group.position.x += .5;
// group.rotation.x += .05;
renderer.render(scene, camera);
}
// Returns a THREE.texture created from a canvas element
function generateSprite() {
var canvas = createCanvasDot();
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
}
// Returns a canvas element with a single dot
function createCanvasDot() {
var canvas = document.createElement('canvas');
canvas.width = spriteWidth;
canvas.height = spriteHeight;
var context = canvas.getContext('2d');
// Arguments: x0, y0, r0 (of the start circle), x1, y1, r1 (of the end circle)
var x0 = y0 = x1 = y1 = spriteWidth / 2;
var crimson = 'rgba(255,0,63,1)';
if (r0.node().value === r1.node().value) {
context.fillStyle = crimson;
context.beginPath();
context.arc(x0, y0, r0.node().value, 0, 2*Math.PI);
context.fill();
} else {
var gradient = context.createRadialGradient(x0, y0, r0.node().value, x1, y1, r1.node().value);
gradient.addColorStop(0, crimson);
gradient.addColorStop(1, 'rgba(255,255,255,0)');
context.fillStyle = gradient;
context.fillRect(0, 0, canvas.width, canvas.height);
}
return canvas
}
// Create a new set of sprites when user selects a new value
function changed() {
// Stop the animation
cancelAnimationFrame(requestId);
// Redraw the sprite with the new parameters
var start = +r0.node().value;
var end = +r1.node().value;
if (start > end) start = r0.node().value = end;
d3.select('.sprite').each(function() {
this.getContext("2d").clearRect(0, 0, spriteWidth, spriteHeight);
this.getContext("2d").drawImage(createCanvasDot(), 0, 0);
});
// Remove all objects from the scene
while (scene.children.length > 0) {
scene.remove(scene.children[0]);
}
// Add a new group of sprites to the scene
scene.add(createGroupOfSprites());
// Restart the animation
render();
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment