How to rotate an image in a canvas? - javascript

I'm trying to draw an image with d3 from a json file. This image data is then put into a canvas context to actually draw the image. I would like to rotate the resulting image but I cant find how. The code I actually use
$(document).ready(function() {
d3.json("resources/image.json").then(function(image) {
var canvas = d3.select("colorImage")
.append("canvas")
.attr("width", 200)
.attr("height", 200);
var context = canvas.node().getContext("2d");
var image_data = context.createImageData(200, 200);
image_data.data.set(image);
context.rotate(60);
context.putImageData(image_data, 0, 0); // <---- image not rotated
context.fillRect( 100, 100, 20, 20 ); // <--- rectangle rotated
}).catch(function(error){
if (error) throw error;
});
})

putImageData() and getImageData() are not affected by the context's transformation matrix.
To rotate it, the easiest is to create an ImageBitmap from it and to draw that ImageBitmap using drawImage():
(async () => {
const canvas = document.getElementById( 'canvas' );
const context = canvas.getContext( '2d' );
// make some noise
const image = new Uint8Array( Uint32Array.from(
{ length: 200 * 200 },
_ => (Math.random() * 0xFFFFFFFF)
).buffer );
const image_data = context.createImageData( 200, 200 );
image_data.data.set( image );
const center_w = canvas.width / 2;
const center_h = canvas.height / 2;
const bitmap = await createImageBitmap( image_data );
context.translate( center_w, center_h );
context.rotate( 60 );
context.translate( -center_w, -center_h );
context.drawImage( bitmap, center_w-100, center_h-100 ); // <---- image rotated
context.lineWidth = 4;
context.strokeStyle = 'green';
context.strokeRect( center_w-100, center_h-100, 200, 200 ); // <--- rectangle rotated
})().catch( console.error );
<canvas id="canvas" height="300" width="300"></canvas>
And for browsers that do not support createImageBitmap() method, you can monkey-patch this exact use quite easily by using an HTMLCanvasElement:
/* if( !window.createImageBitmap ) { */ // for demo we force monkey-patch
monkeyPatchCreateImageBitmap();
/*} */
(async () => {
const canvas = document.getElementById( 'canvas' );
const context = canvas.getContext( '2d' );
// make some noise
const image = new Uint8Array( Uint32Array.from(
{ length: 200 * 200 },
_ => (Math.random() * 0xFFFFFFFF)
).buffer );
const image_data = context.createImageData( 200, 200 );
image_data.data.set( image );
const center_w = canvas.width / 2;
const center_h = canvas.height / 2;
const bitmap = await createImageBitmap( image_data );
context.translate( center_w, center_h );
context.rotate( 60 );
context.translate( -center_w, -center_h );
context.drawImage( bitmap, center_w-100, center_h-100 ); // <---- image rotated
context.lineWidth = 4;
context.strokeStyle = 'green';
context.strokeRect( center_w-100, center_h-100, 200, 200 ); // <--- rectangle rotated
})().catch( console.error );
// minimalistic version that only accepts ImageData, and no clipping
function monkeyPatchCreateImageBitmap() {
window.createImageBitmap = function( source ) {
if( source instanceof ImageData ) {
const dest = document.createElement( 'canvas' );
dest.width = source.width;
dest.height = source.height;
dest.getContext( '2d' ).putImageData( source, 0, 0 );
return Promise.resolve( dest );
}
};
}
<canvas id="canvas" height="300" width="300"></canvas>

Related

Resize canvas to viewport without loosing quality

I am trying to have a canvas fill the entire viewport and resize, whenever the user resizes the browser window.
However, when I try to use a size of 100%, it looses quality.
I am kind of a newb in JavaScript programming.
Can someone please help me?
I figured out a way:
I calculate a scaling factor, by which I scale the size, but not the position of each element drawn.
I do not want to scale the position, because then some elements might be off screen.
Thus I can not use setTransform, as suggested in the comments.
Here is the code I used for calculating the scaling factor.
If someone has improvements, please share them in the comments below!
if (window.screen.width > window.screen.height)
scalingFactor = window.screen.width * window.devicePixelRatio / 1280;
else
scalingFactor = window.screen.height * window.devicePixelRatio / 720;
The easiest way to resize a canvas with losing the data currently on it is to store the data, then resize the canvas (which clears it), then redraw the data into the canvas. That would look something like this:
const data = ctx.getImageData( 0, 0, canvas.width, canvas.height );
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.putImageData( data, 0, 0 );
In its purest, simplest example it would look something like this:
/* This method does the resize by storing the data and redrawing it. It loses any data outside of the screen, though. */
function onResize( event ){
const data = ctx.getImageData( 0, 0, canvas.width, canvas.height );
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.putImageData( data, 0, 0 );
}
/* This method is for drawing some content to help visualisation. */
function onRedraw(){
const r = v => Math.floor(Math.random() * v);
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`;
ctx.fillRect( 0, 0, innerWidth, innerHeight );
ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`;
ctx.fillRect( 0, 0, innerWidth / 2, innerHeight / 2 );
ctx.fillRect( innerWidth / 2, innerHeight / 2, innerWidth / 2, innerHeight / 2 );
}
window.addEventListener( 'resize', onResize );
const canvas = document.createElement( 'canvas' );
const ctx = canvas.getContext( '2d' );
const reset = document.getElementById( 'reset' );
document.body.appendChild( canvas );
onRedraw();
redraw.addEventListener( 'click', onRedraw );
body { padding: 0; margin: 0; overflow: hidden; }
button { position: fixed; top: 20px; left: 20px; z-index: 1; }
<button id="redraw">Redraw</button>
However, you lose data drawn outside the canvas when your window grows and shrinks, as the data is never saved. We could hold a buffer with our graphics, which could simplify things a bit but makes the code slightly more complicated. In this case, we can use a second canvas that holds all our previously drawn maximum sized screen data, just for reference:
/* This method does the resize by storing the data and redrawing it. It retains data outside the screen in a buffer that holds the maximum size ever drawn. */
function onResize( event ){
const data = bufferCtx.getImageData( 0, 0, bufferCanvas.width, bufferCanvas.height );
bufferCanvas.width = Math.max( bufferCanvas.width, innerWidth );
bufferCanvas.height = Math.max( bufferCanvas.height, innerHeight );
bufferCtx.putImageData( data, 0, 0 );
bufferCtx.drawImage( canvas, 0, 0 );
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.drawImage( bufferCanvas, 0, 0 );
}
/* This method is for drawing some content to help visualisation. */
function onRedraw(){
const r = v => Math.floor(Math.random() * v);
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`;
ctx.fillRect( 0, 0, innerWidth, innerHeight );
ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`;
ctx.fillRect( 0, 0, innerWidth / 2, innerHeight / 2 );
ctx.fillRect( innerWidth / 2, innerHeight / 2, innerWidth / 2, innerHeight / 2 );
}
window.addEventListener( 'resize', onResize );
const canvas = document.createElement( 'canvas' );
const ctx = canvas.getContext( '2d' );
const bufferCanvas = document.createElement( 'canvas' );
const bufferCtx = bufferCanvas.getContext( '2d' );
const reset = document.getElementById( 'reset' );
document.body.appendChild( canvas );
onRedraw();
redraw.addEventListener( 'click', onRedraw );
body { padding: 0; margin: 0; overflow: hidden; }
button { position: fixed; top: 20px; left: 20px; z-index: 1; }
<button id="redraw">Redraw</button>
If you'de rather use lose no data at all and 'rescale' the canvas the best it can, perhaps best to use something like this:
/* This method does the resize by just redrawing the canvas from a buffer: */
function onResize( event ){
bufferCanvas.width = canvas.width;
bufferCanvas.height = canvas.height;
bufferCtx.drawImage( canvas, 0, 0 );
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.drawImage( bufferCanvas, 0, 0, canvas.width, canvas.height );
}
/* This method is for drawing some content to help visualisation. */
function onRedraw(){
const r = v => Math.floor(Math.random() * v);
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`;
ctx.fillRect( 0, 0, innerWidth, innerHeight );
ctx.fillStyle = `rgb(${r(255)},${r(255)},${r(255)})`;
ctx.fillRect( 0, 0, innerWidth / 2, innerHeight / 2 );
ctx.fillRect( innerWidth / 2, innerHeight / 2, innerWidth / 2, innerHeight / 2 );
}
window.addEventListener( 'resize', onResize );
const canvas = document.createElement( 'canvas' );
const ctx = canvas.getContext( '2d' );
const bufferCanvas = document.createElement( 'canvas' );
const bufferCtx = bufferCanvas.getContext( '2d' );
const reset = document.getElementById( 'reset' );
document.body.appendChild( canvas );
onRedraw();
redraw.addEventListener( 'click', onRedraw );
body { padding: 0; margin: 0; overflow: hidden; }
button { position: fixed; top: 20px; left: 20px; z-index: 1; }
<button id="redraw">Redraw</button>

Why is HTML5 Canvas blurry?

I have a canvas that is supposed to fill 100% of its parent div and scale height based on a ratio. The image that is placed in it is supposed to fill the canvas's height while scaling its width so it doesn't get distorted. That seems to be working but everything on the canvas is really blurry. Why is this happening and how might I display crisp images, shapes and text?
const photo = 'https://docplayer.net/docs-images/54/10835314/images/page_1.jpg';
const canvasRef = document.getElementById('canvas');
const mugRatioWidth = 2681;
const mugRatioHeight = 1110;
const canvasRatio = mugRatioHeight / mugRatioWidth;
const img = new Image();
const ctx = canvasRef.getContext("2d");
canvasRef.style.width = '100%';
img.src = photo;
img.onload = () => {
const hRatio = canvasRef.width / img.width ;
const vRatio = canvasRef.height / img.height ;
const ratio = Math.min ( hRatio, vRatio );
const centerShift_x = ( canvasRef.width - img.width*ratio ) / 2;
const centerShift_y = ( canvasRef.height - img.height*ratio ) / 2;
ctx.clearRect(0,0,canvasRef.width, canvasRef.height);
ctx.drawImage(img, 0,0, img.width, img.height, centerShift_x,centerShift_y,img.width*ratio, img.height*ratio);
ctx.beginPath();
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.stroke();
ctx.fillText("Text example!", 10, 90);
}
<canvas id='canvas'/>

Canvas createPattern() and fill() with an offset

I'm using the canvas to draw simple rectangles and fill them with a flooring pattern (PNG). But if I utilize a "Camera" script to handle transform offsets for the HTML5 canvas, the rectangle shape moves appropriately, but the fill pattern seems to always draw from a fixed point on the screen (I'm assuming the top left). Is there a way to "nail down" the fill pattern so it always lines up with the rectangle, no matter the canvas transform, or a way to add an offset on fill() that can be calculated elsewhere in the code? I could just use drawImage() of course, but drawing the rectangles is more versatile for my purposes.
sampleDrawFunction(fillTexture, x1, y1, x2, y2) {
// This is oversimplified, but best I can do with a ~10k lines of code
x1 -= Camera.posX;
x2 -= Camera.posX;
y1 = -1 * y1 - Camera.posY;
y2 = -1 * y2 - Camera.posY;
// Coordinates need to be adjusted for where the camera is positioned
var img = new Image();
img.src = fillTexture;
var pattern = this.ctx.createPattern(img, "repeat");
this.ctx.fillStyle = pattern;
// Translate canvas's coordinate pattern to match what the camera sees
this.ctx.save();
this.ctx.translate(Camera.posX - Camera.topLeftX, Camera.posY - Camera.topLeftY);
this.ctx.fillStyle = pattern;
this.ctx.fillRect(x1, y1, x2 - x1, y2 - y1);
this.ctx.restore();
}
Thank you.
Canvas fills (CanvasPatterns and CanvasGradients) are always relative to the context's transformation matrix, so they do indeed default at top left corner of the canvas, and don't really care about where the path using them will be be.
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = begin; //!\ ALWAYS WAIT FOR YOUR IMAGE TO LOAD BEFORE DOING ANYTHING WITH IT!
img.crossOrigin = "anonymous";
img.src = "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png";
function begin() {
const rect_size = 20;
ctx.fillStyle = ctx.createPattern( img, 'no-repeat' );
// drawing a checkerboard of several rects shows that the pattern doesn't move
for ( let y = 0; y < canvas.height; y += rect_size ) {
for ( let x = (y / rect_size % 2) ? rect_size : 0 ; x < canvas.width; x += rect_size * 2 ) {
ctx.fillRect( x, y, rect_size, rect_size );
}
}
}
<canvas id="canvas" width="500" height="500"></canvas>
Now, because they are relative to the context transform matrix, this means we can also move them by changing that transformation matrix:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = begin; //!\ ALWAYS WAIT FOR YOUR IMAGE TO LOAD BEFORE DOING ANYTHING WITH IT!
img.crossOrigin = "anonymous";
img.src = "https://upload.wikimedia.org/wikipedia/commons/f/f7/Cool_bunny_sprite.png";
function begin() {
const rect_size = 244;
const max_speed = 5;
let x_speed = Math.random() * max_speed;
let y_speed = Math.random() * max_speed;
ctx.fillStyle = ctx.createPattern( img, 'repeat' );
let x = 0;
let y = 0;
requestAnimationFrame( anim );
function anim( now ) {
clear();
x = (x + x_speed);
y = (y + y_speed);
if( x > canvas.width || x < -rect_size ) {
x_speed = Math.random() * max_speed * -Math.sign( x_speed );
x = Math.min( Math.max( x, -rect_size ), canvas.width );
}
if( y > canvas.height || y < -rect_size ) {
y_speed = Math.random() * max_speed * -Math.sign( y_speed )
y = Math.min( Math.max( y, -rect_size ), canvas.height );
}
// we change the transformation matrix of our context
ctx.setTransform( 1, 0, 0, 1, x, y );
// we thus always draw at coords 0,0
ctx.fillRect( 0, 0, rect_size, rect_size );
ctx.strokeRect( 0, 0, rect_size, rect_size );
requestAnimationFrame( anim );
}
function clear() {
// since we changed the tranform matrix we need to reset it to identity
ctx.setTransform( 1, 0, 0, 1, 0, 0 );
ctx.clearRect( 0, 0, canvas.width, canvas.height );
}
}
<canvas id="canvas" width="300" height="300"></canvas>
We can even detach the path declaration from the filling by changing the transformation matrix after we declared the sub-path and of course by replacing the shorthand fillRect() with beginPath(); rect(); fill()
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = begin; //!\ ALWAYS WAIT FOR YOUR IMAGE TO LOAD BEFORE DOING ANYTHING WITH IT!
img.crossOrigin = "anonymous";
img.src = "https://upload.wikimedia.org/wikipedia/commons/f/f7/Cool_bunny_sprite.png";
function begin() {
const rect_size = 244;
const max_speed = 5;
let x_speed = Math.random() * max_speed;
let y_speed = Math.random() * max_speed;
ctx.fillStyle = ctx.createPattern( img, 'repeat' );
let x = 0;
let y = 0;
requestAnimationFrame( anim );
function anim( now ) {
clear();
x = (x + x_speed);
y = (y + y_speed);
if( x > canvas.width || x < -rect_size ) {
x_speed = Math.random() * max_speed * -Math.sign( x_speed );
x = Math.min( Math.max( x, -rect_size ), canvas.width );
}
if( y > canvas.height || y < -rect_size ) {
y_speed = Math.random() * max_speed * -Math.sign( y_speed )
y = Math.min( Math.max( y, -rect_size ), canvas.height );
}
// we declare the sub-path first, with identity matrix applied
ctx.beginPath();
ctx.rect( 50, 50, rect_size, rect_size );
// we change the transformation matrix of our context
ctx.setTransform( 1, 0, 0, 1, x, y );
// and now we fill
ctx.fill();
ctx.stroke();
requestAnimationFrame( anim );
}
function clear() {
// since we changed the tranform matrix we need to reset it to identity
ctx.setTransform( 1, 0, 0, 1, 0, 0 );
ctx.clearRect( 0, 0, canvas.width, canvas.height );
}
}
<canvas id="canvas" width="300" height="300"></canvas>
But in your case, it sounds that transforming the whole drawing is the easiest and most idiomatic way to follow. Not too sure why you are modifying your x and y coordinates in relation to the camera. Generally, if we use a camera object it's for the objects in the scene don't have to care about it, and stay relative to the world.

Apply grayscale and sepia filters on mousemove - canvas

I am trying to apply grayscale and sepia filters on canvas at the time of mouseMove.
Am using CanvasRenderingContext2D.filter for applying the filters.
Here's the sample code
var radgrad = this.ctx.createRadialGradient(x, y, 50 / 8, x, y, 50 / 2);
radgrad.addColorStop(0, 'rgb(0, 0, 0)');
radgrad.addColorStop(1, 'rgb(0, 0, 0, 1)');
this.ctx.filter = "grayscale(100%) blur(5px) opacity(50%)";
this.ctx.fillStyle = radgrad;
this.ctx.beginPath();
this.ctx.arc(x, y, 50, 0, Math.PI * 2);
this.ctx.fill();
Problem is when I am trying to apply grayscale am not able to achieve it but the blur(5px) is getting applied.
Any solution how to apply grayscale or sepia filter in the above method.
Here's a sample fiddle
Any lead on the solution will be helpful. Thanks
I am not too clear as to what you want, so I'll assume you want something cumulative, as in moving over the same position twice will apply the filter twice.
To do this, the easiest is to create a CanvasPattern from your image. This way you'll be able to fill sub-path using that image as fillStyle, and in the mean time apply your filters on this new drawing:
const img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Sunset_2007-1.jpg/1024px-Sunset_2007-1.jpg";
img.onload = begin;
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
const rad = 25;
function begin() {
canvas.width = img.width;
canvas.height = img.height;
// first draw the original image
ctx.drawImage( img, 0, 0 );
// create a CanvasPattern from it
const patt = ctx.createPattern(img, 'no-repeat');
// set the fillStyle to this pattern
ctx.fillStyle = patt;
// and the filter
ctx.filter = "grayscale(100%) blur(5px) opacity(50%)";
// now at each mousemove
document.onmousemove = e => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// we just draw a new arc
ctx.beginPath();
ctx.arc( x, y, rad, 0, Math.PI * 2 );
// this will use the filtered pattern
ctx.fill();
};
}
<canvas id="canvas"></canvas>
In case you didn't want it to be cumulative (like a scratch-card), then you could create a single big sub-path and redraw everything at every frame.
const img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Sunset_2007-1.jpg/1024px-Sunset_2007-1.jpg";
img.onload = begin;
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
const rad = 25;
const points = [];
const filter = "grayscale(100%) blur(5px) opacity(50%)";
function begin() {
canvas.width = img.width;
canvas.height = img.height;
const patt = ctx.createPattern(img, 'no-repeat');
ctx.fillStyle = patt;
draw();
// now at each mousemove
document.onmousemove = e => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// store that point
points.push( { x, y } );
// and redraw
draw();
};
}
function draw() {
// remove the filter
ctx.filter = "none";
// so we can draw the background untouched
ctx.drawImage( img, 0, 0 );
// we'll now compose a big sub-path
ctx.beginPath();
points.forEach( ({ x, y }) => {
ctx.moveTo( x, y );
ctx.arc( x, y, rad, 0, Math.PI * 2 )
});
// with the filter
ctx.filter = filter;
ctx.fill();
}
<canvas id="canvas"></canvas>
Note that this code assumes you are on a modern browser which does throttle the mouse events to frame rate. If you are targetting older browsers, you may need to do it yourself.

Implement HTML canvas image tinting function

First time StackOverflow poster here so please be gentle :)
I am trying to implement image tinting in HTML canvas for a game I'm developing. There were a lot of good suggestions here:
How do i tint an image with HTML5 Canvas?
And I successfully implemented one of those, but the image colours looked too washed out. I was impressed with the results here:
http://www.playmycode.com/blog/2011/06/realtime-image-tinting-on-html5-canvas/
And I tried to play with the code but it's simply not working in the fiddle I'm messing with (can't post a link to JS Fiddle as I don't have enough rep):
function generateRGBKs( img ) {
var w = img.width;
var h = img.height;
var rgbks = [];
var canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext("2d");
ctx.drawImage( img, 0, 0 );
var pixels = ctx.getImageData( 0, 0, w, h ).data;
// 4 is used to ask for 3 images: red, green, blue and
// black in that order.
for ( var rgbI = 0; rgbI < 4; rgbI++ ) {
var canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx.drawImage( img, 0, 0 );
var to = ctx.getImageData( 0, 0, w, h );
var toData = to.data;
for (
var i = 0, len = pixels.length;
i < len;
i += 4
) {
toData[i ] = (rgbI === 0) ? pixels[i ] : 0;
toData[i+1] = (rgbI === 1) ? pixels[i+1] : 0;
toData[i+2] = (rgbI === 2) ? pixels[i+2] : 0;
toData[i+3] = pixels[i+3] ;
}
ctx.putImageData( to, 0, 0 );
// image is _slightly_ faster then canvas for this, so convert
var imgComp = new Image();
imgComp.src = canvas.toDataURL();
rgbks.push( imgComp );
}
return rgbks;
}
function generateTintImage( img, rgbks, red, green, blue ) {
var buff = document.createElement( "canvas" );
buff.width = img.width;
buff.height = img.height;
var ctx = buff.getContext("2d");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'copy';
ctx.drawImage( rgbks[3], 0, 0 );
ctx.globalCompositeOperation = 'lighter';
if ( red > 0 ) {
ctx.globalAlpha = red / 255.0;
ctx.drawImage( rgbks[0], 0, 0 );
}
if ( green > 0 ) {
ctx.globalAlpha = green / 255.0;
ctx.drawImage( rgbks[1], 0, 0 );
}
if ( blue > 0 ) {
ctx.globalAlpha = blue / 255.0;
ctx.drawImage( rgbks[2], 0, 0 );
}
return buff;
}
var img = new Image();
img.onload = function() {
var rgbks = generateRGBKs( img );
var tintImg = generateTintImage( img, rgbks, 200, 50, 100 );
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect( 0, 0, 100, 100 );
ctx.drawImage( tintImg, 50, 50 );
}
img.src = "https://pbs.twimg.com/profile_images/413816438701309953/uvZMK_gT_normal.jpeg";
I'm sure it's something stupid I've done and nothing wrong with Joe's code - can anyone help me get it working?
Many thanks :)
Because you're accessing the pixel data the image needs to be on the same domain as the code, or you can try using a base64 src.
img.src = "data:image/jpeg;base64,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";
You're breaking CORS policy
Heres a working JSFiddle

Categories