Last active
December 16, 2024 12:52
-
-
Save racsoraul/49563404361e13cb813b to your computer and use it in GitHub Desktop.
SVG Plant Generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<svg viewBox="0 0 550 400" preserveAspectRatio="xMidYMax meet"> | |
<g id="stems" fill="none" stroke="green"></g> | |
<g id="leaves"></g> | |
</svg> | |
<br /> | |
<button id="create">Create</button> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on the plant generator by Kevin Lindsey | |
// http://www.kevlindev.com/alife/plants/plants.svg | |
class EventManager { | |
constructor() { | |
this.eventLookup = {}; | |
} | |
off(event, callback) { | |
var listeners = this.eventLookup[event]; | |
if (event === "*") this.eventLookup = {}; | |
else if(!callback) this.eventLookup[event] = []; | |
else _.remove(listeners, { callback }); | |
} | |
on(event, callback, scope) { | |
var listeners = this.eventLookup[event]; | |
if (!listeners) this.eventLookup[event] = listeners = []; | |
listeners.push({ callback, scope }); | |
return () => _.remove(listeners, { callback }); | |
} | |
once(event, callback, scope) { | |
var on = (...data) => { | |
this.off(event, on); | |
callback.apply(scope, data); | |
}; | |
return this.on(event, on); | |
} | |
fire(event, ...data) { | |
var listeners = this.eventLookup[event]; | |
if (!listeners) return; | |
listeners.forEach(list => { | |
try { | |
return list.callback.apply(list.scope, data); | |
} catch(e) { | |
return _.isError(e) ? e : new Error(e); | |
} | |
}); | |
} | |
} | |
var events = new EventManager(); | |
var ns = "http://www.w3.org/2000/svg"; | |
var d = "M0,0 Q5,-5 10,0 5,5 0,0z"; | |
var stems = $("#stems"); | |
var leaves = $("#leaves"); | |
var svg = $("svg"); | |
var leafCount = 30; | |
var plants = 10; | |
var centerX = 275; | |
var offsetX = 175; | |
$("#create").on("click", generate); | |
generate(); | |
function generate() { | |
leaves.empty(); | |
stems.empty(); | |
_.times(plants, createPlant); | |
stems.children().each(function() { | |
var tween = TweenMax.to(this, _.random(2, 4), { | |
drawSVG: true, | |
delay: _.random(2), | |
onStart: () => TweenLite.set(this, { opacity: 1 }), | |
onUpdate: () => events.fire(this.id, tween.progress()) | |
}); | |
}); | |
} | |
function createPlant() { | |
var points = createPoints(); | |
var stem = createPath(stems); | |
var length = points.length; | |
var values = points.map(point => `${point.x},${point.y}`); | |
var height = points[length - 1].y; | |
var id = _.uniqueId("grow"); | |
TweenLite.set(stem, { | |
opacity: 0, | |
drawSVG: 0, | |
attr: { id, d: `M${values.join(" ")}` } | |
}); | |
for (var i = 0; i < leafCount; i++) { | |
var point = points[length - 1 - i]; | |
var scale = { | |
x: 1 + 0.1 * i, | |
y: 1 + 0.05 * i | |
}; | |
createLeaf(point, scale, height, id); | |
} | |
} | |
function createLeaf(point, scale, height, grow) { | |
var leaf = createPath(leaves); | |
var start = height / point.y; | |
var off = events.on(grow, growLeaf); | |
function growLeaf(growth) { | |
if (growth >= start) { | |
// Remove listener | |
off(); | |
TweenLite.set(leaf, { | |
x: point.x, | |
y: point.y, | |
scaleX: scale.x, | |
scaleY: scale.y, | |
rotation: _.random(180) - 180, | |
fill: `rgb(0,${_.random(110, 160)},0)`, | |
attr: { d } | |
}); | |
TweenLite.from(leaf, 1, { scale: 0 }); | |
} | |
} | |
} | |
function createPoints() { | |
var x = _.random(centerX - offsetX, centerX + offsetX); | |
var y = 400; | |
var dy = 5; | |
var offset = 0.007; | |
var count = _.random(30, 65); | |
var points = [{ x, y }]; | |
for (var i = 1; i <= count; i++) { | |
points.push({ | |
x: points[i - 1].x + i * offset * (_.random(21) - 10), | |
y: 395 - dy * i | |
}); | |
} | |
return points; | |
} | |
function createPath(parent) { | |
return $(document.createElementNS(ns, "path")).appendTo(parent); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/TweenMax.min.js"></script> | |
<script src="//s3-us-west-2.amazonaws.com/s.cdpn.io/16327/DrawSVGPlugin.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.9.3/lodash.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background: #eee; | |
} | |
* { | |
box-sizing: border-box; | |
} | |
svg { | |
width: 100%; | |
height: 100%; | |
position: absolute; | |
} | |
button { | |
position: absolute; | |
top: 15px; | |
left: 15px; | |
background: yellowgreen; | |
border: 1px solid rgba(0,0,0,0.2); | |
padding: 10px 16px; | |
font-weight: bold; | |
color: #222; | |
transition: all 0.1s; | |
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37); | |
&:focus { | |
outline: none; | |
} | |
&:hover { | |
border: 1px solid rgba(0,0,0,0.5); | |
color: black; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment