Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Here is a simple hair simulation that seems to be what you are looking for. The basic idea is to draw a bezier curve (in this case I use two curves to provide thickness for the hair). The curve will have a base, a bending point, and a tip. I set the bending point halfway up the hair. The tip of the hair will rotate about the axis of the base of the hair in response to mouse movement.</p> <p>Place this code in a script tag below the canvas element declaration.</p> <pre><code> function Point(x, y) { this.x = x; this.y = y; } function Hair( ) { this.height = 100; // hair height this.baseWidth = 3; // hair base width. this.thickness = 1.5; // hair thickness this.points = {}; this.points.base1 = new Point(Math.random()*canvas.width, canvas.height); // The point at which the hair will bend. I set it to the middle of the hair, but you can adjust this. this.points.bendPoint1 = new Point(this.points.base1.x-this.thickness, this.points.base1.y - this.height / 2) this.points.bendPoint2 = new Point(this.points.bendPoint1.x, this.points.bendPoint1.y-this.thickness); // complement of bendPoint1 - we use this because the hair has thickness this.points.base2 = new Point(this.points.base1.x + this.baseWidth, this.points.base1.y) // complement of base1 - we use this because the hair has thickness } Hair.prototype.paint = function(mouseX, mouseY, direction) { ctx.save(); // rotate the the tip of the hair var tipRotationAngle = Math.atan(Math.abs(this.points.base1.y - mouseY)/Math.abs(this.points.base1.x - mouseX)); // if the mouse is on the other side of the hair, adjust the angle if (mouseX &lt; this.points.base1.x) { tipRotationAngle = Math.PI - tipRotationAngle; } // if the mouse isn't close enough to the hair, it shouldn't affect the hair if (mouseX &lt; this.points.base1.x - this.height/2 || mouseX &gt; this.points.base1.x + this.height/2 || mouseY &lt; this.points.base1.y - this.height || mouseY &gt; this.points.base1.y) { tipRotationAngle = Math.PI/2; // 90 degrees, which means the hair is straight } // Use the direction of the mouse to as a lazy way to simulate the direction the hair should bend. // Note that in real life, the direction that the hair should bend has nothing to do with the direction of motion. It actually depends on which side of the hair the force is being applied. // Figuring out which side of the hair the force is being applied is a little tricky, so I took this shortcut. // If you run your finger along a comb quickly, this approximation will work. However if you are in the middle of the comb and slowly change direction, you will notice that the force is still applied in the opposite direction of motion as you slowly back off the set of tines. if ((mouseX &lt; this.points.base1.x &amp;&amp; direction == 'right') || (mouseX &gt; this.points.base1.x &amp;&amp; direction == 'left')) { tipRotationAngle = Math.PI/2; // 90 degrees, which means the hair is straight } var tipPoint = new Point(this.points.base1.x + this.baseWidth + this.height*Math.cos(tipRotationAngle), this.points.base1.y - this.height*Math.sin(tipRotationAngle)); ctx.beginPath(); ctx.moveTo(this.points.base1.x, this.points.base1.y); // start at the base ctx.bezierCurveTo(this.points.base1.x, this.points.base1.y, this.points.bendPoint1.x, this.points.bendPoint1.y, tipPoint.x, tipPoint.y); // draw a curve to the tip of the hair ctx.bezierCurveTo(tipPoint.x, tipPoint.y, this.points.bendPoint2.x, this.points.bendPoint2.y, this.points.base2.x, this.points.base2.y); // draw a curve back down to the base using the complement points since the hair has thickness. ctx.closePath(); // complete the path so we have a shape that we can fill with color ctx.fillStyle='rgb(0,0,0)'; ctx.fill(); ctx.restore(); } // I used global variables to keep the example simple, but it is generally best to avoid using global variables window.canvas = document.getElementById('myCanvas'); window.ctx = canvas.getContext('2d'); ctx.fillStyle = 'rgb(200,255,255)'; // background color window.hair = []; window.prevClientX = 0; for (var i = 0; i &lt; 100; i++) { hair.push(new Hair()); } // initial draw ctx.fillRect(0,0,canvas.width,canvas.height); // clear canvas for (var i = 0; i &lt; hair.length; i++) { hair[i].paint(0, 0, 'right'); } window.onmousemove = function(e) { ctx.fillRect(0,0,canvas.width,canvas.height); // clear canvas for (var i = 0; i &lt; hair.length; i++) { hair[i].paint(e.clientX, e.clientY, e.clientX &gt; window.prevClientX ? 'right' : 'left'); } window.prevClientX = e.clientX; } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload