<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<div class="container">
<canvas class="main" id="game"></canvas>
<canvas class="main" id="light"></canvas>
</div>
html, body{
padding:0px;
margin:0px;
background:#cdcdcd;
font-family: 'Karla', sans-serif;
color:#FFF;
}
canvas{
display:block;
box-shadow:0px 2px 5px rgba(0, 0, 0, 0.25);
border-radius:2px;
position:absolute;
top:50%; left:50%;
transform:translateX(-50%) translateY(-50%);
animation: fade 2s;
}
#game{ z-index: 1000;}
@keyframes fade {
from { opacity: 0; top:100%; }
to { opacity: 1; }
}
var ctxGame = document.querySelector('#game').getContext('2d')
var ctxLight = document.querySelector('#light').getContext('2d')
var blocks = parseMap(`
----------------------------
----------------------------
----------------------------
----------------------------
----------------------------
---B-----B-------B-----B----
---B-------------B-----B----
---B-----B--BBB--BBB--BBB---
---B-----B--B-B--B-B---B----
---BBBB--B--BBB--B-B---B----
--------------B-------------
------------BBB-------------
----------------------------
----------------------------
----------------------------
----------------------------`, 16, 16)
var lights = [new Light(true, 114, 49), new Light(false, 250, 50)]
render()
function render(){
setTimeout(render, 1000/60)
// Draw Game / Blocks
ctxGame.clearRect(0, 0, ctxGame.canvas.width, ctxGame.canvas.height)
for(var block of blocks) block.draw()
// Draw clearRect
ctxLight.globalCompositeOperation = 'source-over';
ctxLight.fillRect(0, 0, ctxLight.canvas.width, ctxLight.canvas.height)
for(var light of lights) light.draw()
}
function parseMap(map, gridWidth, gridHeight){
var blocks = []
var rows = map.trim().split(' ')
for(var row in rows){
var columns = rows[row].trim().split('')
for(var column in columns){
if( columns[column] == 'B'){
blocks.push(new Block(column*gridWidth, row*gridHeight))
}
if( columns[column] == 'L'){
lights.push(new Light(false, column*gridWidth, row*gridHeight))
}
}
}
var width = gridWidth*columns.length
var height = gridHeight*rows.length
canvasSize(ctxGame.canvas, width, height)
canvasSize(ctxLight.canvas, width, height)
return blocks
}
function Block(x, y){
this.x = x
this.y = y
this.width = 16
this.height = 16
this.draw = function() {
ctxGame.globalCompositeOperation = 'source-over';
ctxGame.fillStyle = '#FF5353'
ctxGame.fillRect(this.x, this.y, this.width, this.height)
}
}
function Light(follow, x = 0, y = 0, color='white'){
var that = this
this.x = x
this.y = y
this.color = color
this.size = 200
if(follow){
ctxGame.canvas.addEventListener('mousemove', function(e){
that.setPos(e.offsetX, e.offsetY)
})
}
this.ctx = document.createElement('canvas').getContext('2d')
this.ctx.canvas.classList.add('demo')
//document.body.append(this.ctx.canvas)
canvasSize(this.ctx.canvas, ctxGame.canvas.width, ctxGame.canvas.height)
this.setPos = function(x, y){
this.x = x
this.y = y
}
this.draw = function() {
// Draw a circle
this.ctx.globalCompositeOperation = 'source-over';
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
var g = this.ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size);
g.addColorStop(1, 'transparent');
g.addColorStop(0, this.color);
this.ctx.fillStyle = g;
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.size, 0, Math.PI*2, true);
this.ctx.fill();
//Draw the shadows
this.ctx.fillStyle = "black"
this.ctx.globalCompositeOperation = 'destination-out'
for(var block of blocks){
var corners = {
topLeft: { x: block.x, y: block.y },
topRight: { x: block.x + block.width, y: block.y },
btmLeft: { x: block.x, y: block.y + block.height },
btmRight: { x: block.x + block.width, y: block.y + block.height }
}
var points = []
for(var corner in corners){
var dist = distance(corners[corner], {x: this.x, y:this.y })
// What a hack
if(dist >= this.size) continue
var slopeX = (corners[corner].x - this.x)/dist
var slopeY = (corners[corner].y - this.y)/dist
var length = this.size - dist
points.push({
dist: dist,
inside: { x: corners[corner].x, y: corners[corner].y },
outside: { x: corners[corner].x+slopeX*length, y: corners[corner].y+slopeY*length }
})
// ctxGame.beginPath()
// ctxGame.moveTo(corners[corner].x, corners[corner].y)
// ctxGame.lineTo(corners[corner].x+slopeX*length, corners[corner].y+slopeY*length)
// ctxGame.strokeStyle = '#444'
// ctxGame.stroke()
}
// VERY HARDCODED UNTIL I GET A BIT BETTER AT MATHING :]
if(points.length == 4){
points.sort(function(a, b){ return a.dist - b.dist })
// Debugging
// this.ctx.fillText(0, points[0].inside.x, points[0].inside.y)
// this.ctx.fillText(1, points[1].inside.x, points[1].inside.y)
// this.ctx.fillText(2, points[2].inside.x, points[2].inside.y)
// Check if within a cross
var cross = (this.x > block.x && this.x < block.x + block.width)
|| (this.y > block.y && this.y < block.y + block.height)
this.ctx.beginPath()
this.ctx.moveTo(points[1].inside.x, points[1].inside.y)
this.ctx.lineTo(points[0].inside.x, points[0].inside.y)
if(cross){
this.ctx.lineTo(points[0].outside.x, points[0].outside.y)
} else {
this.ctx.lineTo(points[2].inside.x, points[2].inside.y)
}
// Outside
this.ctx.lineTo(points[2].outside.x, points[2].outside.y)
this.ctx.lineTo(points[3].outside.x, points[3].outside.y)
this.ctx.lineTo(points[1].outside.x, points[1].outside.y)
this.ctx.fill()
}
}
ctxLight.globalCompositeOperation = 'destination-out'
ctxLight.drawImage(this.ctx.canvas, 0, 0)
}
}
function distance(p1, p2){
return Math.sqrt(Math.pow((p2.x - p1.x),2) + Math.pow((p2.y - p1.y),2) )
}
function canvasSize(canvas, width, height){
canvas.width = width
canvas.height = height
canvas.style.width = width + 'px'
canvas.style.height = height + 'px'
}