Skip to content

Instantly share code, notes, and snippets.

@tomgp
Created November 14, 2017 11:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tomgp/10071ddfd8591a8bf416dc8773f5a251 to your computer and use it in GitHub Desktop.
Save tomgp/10071ddfd8591a8bf416dc8773f5a251 to your computer and use it in GitHub Desktop.
canvas kaleidoscope
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>kaleidoscope</title>
<style>
body{
font-family: sans-serif;
}
.ui-label{
display: inline-block;
width: 100px;
}
.state-ui{
max-width:500px;
width:100%;
}
.input-value{
display: inline-block;
width: 100px;
}
</style>
</head>
<body>
<ul>
<li>
<span class="ui-label">offsetX</span>
<input class="state-ui" data-targetprop="offsetX" type="range" min="0" max="1500">
<span data-targetprop="offsetX" class="input-value"></span>
</li>
<li>
<span class="ui-label">offsetY</span>
<input class="state-ui" data-targetprop="offsetY" type="range" min="0" max="3000">
<span data-targetprop="offsetY" class="input-value"></span>
</li>
<li>
<span class="ui-label">offsetRotation</span>
<input class="state-ui" data-targetprop="offsetRotation" type="range" min="0" max="6.28" step="0.001">
<span data-targetprop="offsetRotation" class="input-value"></span>
</li>
<li>
<span class="ui-label">slices</span>
<input class="state-ui" data-targetprop="slices" type="range" min="2" max="30" step="2">
<span data-targetprop="slices" class="input-value"></span>
</li>
<li>
<span class="ui-label">zoom</span>
<input class="state-ui" data-targetprop="zoom" type="range" min="0.01" max="2" step="0.01">
<span data-targetprop="zoom" class="input-value"></span>
</li>
</ul>
<canvas></canvas>
</body>
<script>
const HALF_PI = Math.PI/2;
const TWO_PI = Math.PI*2;
const state = {
offsetRotation: 0.0,
offsetScale: 1.0,
offsetX: 0.0,
offsetY: 0.0,
radius: 300,
slices: 8, //should be divisible by 2
zoom: 0.5,
imageURL: 'http://assets.vogue.international.s3.amazonaws.com/projects/trends-2017/fullimages/oscar-de-la-renta-animales-fullsize.jpg'
};
const canvas = document.querySelector('canvas');
canvas.width = state.radius * 2;
canvas.height = state.radius * 2;
const initUI = (props)=>{
Object.keys(props)
.forEach((key)=>{
try{
document.querySelector('span[data-targetprop="'+key+'"]' )
.textContent = props[key];
document.querySelector('input[data-targetprop="'+key+'"]' )
.value = props[key];
}catch(e){
}
});
}
const draw = (props)=>{
const context = document.querySelector('canvas').getContext('2d');
context.fillStyle = context.createPattern(props.image, 'repeat');
const scale = props.zoom * (props.radius / Math.min(props.image.width, props.image.height));
const step = TWO_PI/state.slices;
const cx = props.image.width/2;
for(let i=0; i<props.slices; i++){
context.save();
context.translate(props.radius, props.radius);
context.rotate(i * step)
context.beginPath();
context.moveTo(-0.5, -0.5);
context.arc( 0, 0, props.radius, step * -0.51, step * 0.51)
context.lineTo(0.5, 0.5)
context.closePath()
context.rotate(HALF_PI)
context.scale(scale, scale)
context.scale([-1,1][i % 2], 1)
context.translate(props.offsetX - cx, props.offsetY)
context.rotate(props.offsetRotation)
context.scale(props.offsetScale, props.offsetScale)
context.fill()
context.restore()
}
}
initUI(state);
var img = new Image();
state.image = img;
img.onload = () => {
draw(state);
document.querySelectorAll('.state-ui')
.forEach((node)=>{
node.oninput = (e)=>{
state[node.dataset.targetprop] = e.target.value;
draw(state);
document.querySelector('span[data-targetprop="'+node.dataset.targetprop+'"]' )
.textContent = e.target.value;
};
})
};
img.src = state.imageURL;
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment