Skip to content

Instantly share code, notes, and snippets.

@nbremer
Last active February 10, 2020 11:01
Show Gist options
  • Save nbremer/cbf61944aeb3204d3e4986ea645afc2b to your computer and use it in GitHub Desktop.
Save nbremer/cbf61944aeb3204d3e4986ea645afc2b to your computer and use it in GitHub Desktop.
Color blending - Infinity showcase

This is one of the color blending examples of my blog on Beautiful color blending effects with SVGs & D3

In this small showcase you can see the effect of using a mix-blend-mode on your SVG elements. Every 3 seconds it switches to a different set of colors or switches from "screen" to "multiply" mode and there are 7 different sets before it begins anew. In one ofthe 7 sets, it sets no blend mode and you can see with your own eyes that that version definitely has less magic :)

This example is heavily based on the wonderful particle demo of Sketch.js. I fell in love with it when I first saw it and really wanted to use it in my OpenVis presentation. Because I already thought it was perfect I only made the changes to make it run in an infinity symbol (for infinity) instead of attaching it to a mouse and to loop through several sets of colors and both the screen and multiply blend modes. Also, since I was giving a talk of using SVGs in non-standard ways in D3 I had to adjust the code and make it work with D3 alone. Performance wise, not the better option I know.

Another color blending example can be found here

Built with blockbuilder.org

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- D3.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300' rel='stylesheet' type='text/css'>
<style>
html { font-size: 62.5%; }
.colorStatus {
font-family: 'Open Sans';
font-size: 2rem;
fill: #989898;
font-weight: 300;
}
</style>
</head>
<body>
<div id="infinityChart" style="text-align: center;"></div>
<script language="javascript" type="text/javascript">
///////////////////////////////////////////////////////////////////////////
//////////////////// Set up and initiate svg containers ///////////////////
///////////////////////////////////////////////////////////////////////////
var margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
};
var width = window.innerWidth - margin.left - margin.right - 10,
height = window.innerHeight - margin.top - margin.bottom - 20;
//SVG container
var svg = d3.select('#infinityChart')
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + (margin.left + width/2) + "," + (margin.top + height/2) + ")");
//Grey background color
d3.select("body").style("background", "#262626");
//Reset the overall font size
var newFontSize = width * 62.5 / 2000;
d3.select("html").style("font-size", newFontSize + "%");
//Circle ranges
var circeRanges = [8 * width/2000, 30 * width/2000];
var forceRanges = [6 * width/2000, 10 * width/2000];
//Blend mode info
svg.append("text")
.attr("class", "colorStatus")
.attr("x", 0)
.attr("y", height/2-10)
.style("text-anchor", "middle")
.text("mix-blend-mode: screen");
///////////////////////////////////////////////////////////////////////////
//////////////////////////// Set up infinity path /////////////////////////
///////////////////////////////////////////////////////////////////////////
//But adjusted for D3 and made to move along a path
var ID = 0, //makes all particles unique
counter = 0, //counter for the infinity path
colors = [ '#69D2E7', '#A7DBD8', '#E0E4CC', '#F38630', '#FA6900', '#FF4E50', '#F9D423' ], //first colors
colorMode = "screen", //first blend mode
particles = [];
//Create the infinity path
//Formula from http://gamedev.stackexchange.com/questions/43691/how-can-i-move-an-object-in-an-infinity-or-figure-8-trajectory
var infScale = width/2*0.7;
var x, y, scale;
var infinityPath = [];
for (var i = 0; i < 209; i++) {
t = i*0.03;
scale = 2 / (3 - Math.cos(2*t));
x = scale * Math.cos(t);
y = scale * Math.sin(2*t) / 2;
//console.log("i: " + i + " x:" + x + " y:" + y);
infinityPath.push({x: x*infScale, y: y*infScale});
}//for i
///////////////////////////////////////////////////////////////////////////
//////////////////// Make the circles move in a pattern ///////////////////
///////////////////////////////////////////////////////////////////////////
//Wrapper for the circles
var circleWrapper = svg.append("g")
.attr("class", "circleWrapper")
.style("isolation", "isolate");
function runInfinity() {
//Create new particles
for (var j = 0; j < Math.round(Math.random()*16); j++) spawn(infinityPath[counter].x, infinityPath[counter].y);
//Remove non-alive particles
particles = particles.filter(function(d) { return d.alive; });
//DATA JOIN
//Join new data with old elements, if any
var circleGroup = circleWrapper.selectAll(".particle")
.data(particles, function(d) { return d.id; });
//UPDATE
circleGroup
.style("mix-blend-mode", colorMode)
.each(move)
.transition("move").duration(50).ease("linear")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.radius; });
//ENTER
circleGroup
.enter().append("circle")
.attr("class", "particle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.style("fill", function(d) { return d.color; })
.style("mix-blend-mode", colorMode)
.attr("r", function(d) { return d.radius; });
//EXIT
circleGroup.exit().remove();
counter = (counter + 1)%infinityPath.length;
}//runInfinity
//Create an interval that runs along the infinity path
var loopInfinity = setInterval(runInfinity , 50);
///////////////////////////////////////////////////////////////////////////
/////////////// Functions to create and move the particles ////////////////
///////////////////////////////////////////////////////////////////////////
//Code heavily based on http://codepen.io/soulwire/pen/foktm
//Calculates new position
function move(d) {
d.x += d.vx;
d.y += d.vy;
d.vx *= d.drag;
d.vy *= d.drag;
d.theta += getRandomNumber( -0.5, 0.5 ) * d.wander;
d.vx += Math.sin( d.theta ) * 0.5;
d.vy += Math.cos( d.theta ) * 0.5;
d.radius *= d.age;
d.alive = d.radius > 0.5;
}//move
//Create a particle
function spawn ( x, y ) {
//Play around with these numbers to get different effects
particle = {
x: x,
y: y,
id: ID,
alive: true,
radius: getRandomNumber( circeRanges[0], circeRanges[1] ),
wander: getRandomNumber( 1, 1.5 ),
color: colors[ Math.round( getRandomNumber(0, colors.length-1)) ],
drag: getRandomNumber( 0.2, 0.99 ),
age: getRandomNumber( 0.92, 0.98 ),
theta: getRandomNumber( 0, 2 * Math.PI ),
force: getRandomNumber( forceRanges[0], forceRanges[1] )
};
ID += 1;
particle.vx = Math.sin( particle.theta ) * particle.force;
particle.vy = Math.cos( particle.theta ) * particle.force;
particles.push( particle );
}//spawn
function getRandomNumber(start, end) {
return ((Math.random() * (end-start)) + start);
}
///////////////////////////////////////////////////////////////////////////
////////////// Functions to move through different blend modes ////////////
///////////////////////////////////////////////////////////////////////////
var colorCounter = 0;
function switchColors() {
colorCounter = (colorCounter+1)%7;
switch (colorCounter) {
case 0:
screenMode();
break;
case 1:
noMode();
break;
case 2:
screenModeRainbow();
break;
case 3:
screenModeGreen();
break;
case 4:
multiplyModeGreen();
break;
case 5:
multiplyModeRainbow();
break;
case 6:
multiplyModePurple();
break;
}//switch colorCounter
}//switchColors
var loopColors = setInterval(switchColors, 3000);
function screenMode() {
colorMode = "screen";
colors = ['#69D2E7', '#A7DBD8', '#E0E4CC', '#F38630', '#FA6900', '#FF4E50', '#F9D423'];
d3.select("body").transition().duration(500).style("background", "#262626");
d3.select(".colorStatus").text("mix-blend-mode: screen");
}//screenMode
function noMode() {
colorMode = "none";
colors = ['#69D2E7', '#A7DBD8', '#E0E4CC', '#F38630', '#FA6900', '#FF4E50', '#F9D423'];
d3.select("body").transition().duration(500).style("background", "#262626");
d3.select(".colorStatus").text("mix-blend-mode: none");
}//noMode
function screenModeRainbow() {
colorMode = "screen";
colors = ["#2c7bb6", "#00a6ca","#00ccbc","#90eb9d","#ffff8c","#f9d057","#f29e2e","#e76818","#d7191c"];
d3.select("body").transition().duration(500).style("background", "#262626");
d3.select(".colorStatus").text("mix-blend-mode: screen");
}//multiplyModeRainbow
function screenModeGreen() {
colorMode = "screen";
colors = ['#1B676B', '#519548', '#88C425', "#BEF202", "#EAFDE6"];
d3.select("body").transition().duration(500).style("background", "#262626");
d3.select(".colorStatus").text("mix-blend-mode: screen");
}//screenModeGreen
function multiplyModeGreen() {
colorMode = "multiply";
colors = ['#1B676B', '#519548', '#88C425', "#BEF202", "#EAFDE6"];
d3.select("body").transition().duration(500).style("background", "white");
d3.select(".colorStatus").text("mix-blend-mode: multiply");
}//multiplyModeGreen
function multiplyModeRainbow() {
colorMode = "multiply";
colors = ["#2c7bb6", "#00a6ca","#00ccbc","#90eb9d","#ffff8c","#f9d057","#f29e2e","#e76818","#d7191c"];
d3.select("body").transition().duration(500).style("background", "white");
d3.select(".colorStatus").text("mix-blend-mode: multiply");
}//multiplyModeRainbow
function multiplyModePurple() {
colorMode = "multiply";
colors = ['#490A3D', '#BD1550', '#E97F02', "#F8CA00", "#8A9B0F"];
d3.select("body").transition().duration(500).style("background", "#F8E8E8");
d3.select(".colorStatus").text("mix-blend-mode: multiply");
}//multiplyModePurple
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment