Skip to content

Instantly share code, notes, and snippets.

@erikhazzard
Created September 16, 2016 00:14
Show Gist options
  • Save erikhazzard/ad616265e709214bd5d2ebef3f04b6d4 to your computer and use it in GitHub Desktop.
Save erikhazzard/ad616265e709214bd5d2ebef3f04b6d4 to your computer and use it in GitHub Desktop.
Fog of War SVG Filter Mask Example
license: mit
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<style>
html, body {
background: #ffffff;
font-family: Helvetica, Arial, Tahoma, sans-serif;
margin: 0;
padding: 0;
}
path {
fill: none;
stroke: #343434;
}
.result {
fill: none;
stroke: #343434;
stroke-width: 4px;
}
h1,h2,h3 {
padding-left: 140px;
padding-top: 0px;
}
</style>
</head>
<body>
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink= 'http://www.w3.org/1999/xlink' height=1500 width=1050>
<defs>
<filter id="filter-map" x="-0.15000001" y="-0.15000001" width="1.3" height="1.3" color-interpolation-filters="sRGB" >
<feGaussianBlur stdDeviation="8" in="SourceGraphic" result="result1" id="feGaussianBlur4202" />
<feTurbulence type="fractalNoise" baseFrequency="0.1" numOctaves="3" result="result0" id="feTurbulence4204" />
<feDisplacementMap in2="result0" scale="20" xChannelSelector="R" yChannelSelector="G" in="result1" result="result2" id="feDisplacementMap4206" />
<feGaussianBlur stdDeviation="1" in="SourceGraphic" result="result4" id="feGaussianBlur4208" />
<feComposite in2="result2" operator="arithmetic" k1="0.2" k2="-0.15" k3="0.8" k4="0" in="result4" result="result5" id="feComposite4210" />
</filter>
</defs>
</svg>
<h3>Fog of War - Click to remove fog</h3>
<em>Note: this is not designed to be performant; this is just an example of how filters and masks can be used</em>
<script src='http://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js'></script>
<script>
(function setupMap(){
// 0. setup groups
// ----------------------------------
var svg = d3.select('svg');
var wrapper = svg.append('g').attr({ 'class': 'map-wrapper'});
var background = wrapper.append('g').attr({ 'class': 'background'});
var characters = wrapper.append('g').attr({ 'class': 'characters'});
// 1. Setup the mask
// ----------------------------------
var maskGroup = svg.select('defs').append('mask')
.attr({ id: 'map-mask' });
// 2. Add map images
// ----------------------------------
// fog version - grayscaled and dark
var fogMap = background.append("image")
.attr({
// TODO: use difference background image
'xlink:href': 'http://vasir-assets.s3.amazonaws.com/fog-of-war/map-dark.jpg',
'preserveAspectRatio': 'none',
'class': 'fog', x: 0, y: 0,
height: '100%', width: '100%'
});
// full version
var visibleMap = background.append("image")
.attr({
// TODO: use difference background image
'xlink:href': 'http://vasir-assets.s3.amazonaws.com/fog-of-war/map.jpg',
'preserveAspectRatio': 'none',
'class': 'visibleArea', x: 0, y: 0,
height: '100%', width: '100%'
})
.style({
// fill with full version of map
fill: '#336699', mask: 'url(#map-mask)'
});
// 3. Add a starting circle to break the fog
// ----------------------------------
// create a masked path to show visible nodes
var vertices = [];
function updateFog(){
var masks = maskGroup.selectAll('.breakFog')
.data(vertices);
var newMasks = masks.enter()
.append('circle')
.style({
fill: '#000000',
opacity: 1
});
// update existing masks
masks
.attr({
'class': 'breakFog',
cx: function(d){
console.log(d);
return d.x; },
cy: function(d){ return d.y; },
filter: 'url(#filter-map)',
r: function(d){
var r = 220;
return r;
}
}).style({ fill: '#ffffff', opacity: 1 });
masks.exit().remove();
// add sprites
// ------------------------------
/*
var sprites = characters.selectAll('.character')
.data(vertices);
var races = ['darkelf.gif', 'human.gif', 'elf.gif', 'mimirian.gif'];
sprites.enter()
.append("image")
.attr({
'class': 'character',
'xlink:href': 'http://vasir-assets.s3.amazonaws.com/fog-of-war/' +
races[vertices.length % races.length],
'preserveAspectRatio': 'none',
x: function(d){ return d.x - 53/2; },
y: function(d){ return d.y - 53/2; },
height: 53, width: 53
});
sprites.exit().remove();
*/
};
// Call it immediately
updateFog();
// then, call whenever a point is added
wrapper.on('click', function(){
vertices.push({
x: d3.event.clientX,
y: d3.event.clientY
});
updateFog();
});
wrapper.append('rect').attr({ x:0, y: 0, width: 20, height: 20 }).style({ }).on('click', function(){
setTimeout(function(){
vertices.pop(); vertices.pop();
updateFog()
}, 200);
})
let clickedI = 0;
let coords = [
[100, 200],
[400, 20],
[500, 400],
[100, 400],
[670, 600],
[800, 400],
[1000, 200],
[700, 50],
[500, 100]
]
setTimeout(function () {
coords.forEach(function (d, i) {
setTimeout(function () {
console.log('clicked', i);
vertices.push({
x: coords[i][0],
y: coords[i][1]
});
updateFog()
}, i * 400);
clickedI++;
})
}, 500)
})();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment