Grass like smoothing animation on beziercurve?

This is what I'm trying to achieve - GRASS Animation (Desired Animation)

Here is the project - My hair animation

This is the more structured code of the above code. My hair animation (by markE). - markE animation code

PROBLEM: -

I can give movements to the hairs, but the animation should be more like wavy grass, such as freeflowing. It is not very smooth now. What can be done to make the hair more natural. Please provide me a small sample if possible.

<canvas id="myCanvas" width="500" height="500" style="background-color: antiquewhite" ></canvas> 

JAVASCRIPT

 //mouse position var x2=0; var y2=0; window.addEventListener("mousemove",function(){moving(event);init()},false) //these variables define the bend in our bezier curve var bend9=0; var bend8=0; var bend7=0; var bend6=0; var bend5=0; var bend4=0; var bend3=0; var bend2=0; var bend1=0; //function to get the mouse cordinates function moving(event) { bend_value();//this function is defined below try { x2 = event.touches[0].pageX; y2 = event.touches[0].pageY; } catch (error) { try { x2 = event.clientX; y2 = event.clientY; } catch (e) { } } try { event.preventDefault(); } catch (e) { } if(between(y2,204,237) && between(x2,115,272)) { console.log("Xmove="+x2,"Ymove="+y2) } } //function for declaring range of bezier curve function between(val, min, max) { return val >= min && val <= max; } (function() { hair = function() { return this; }; hair.prototype={ draw_hair:function(a,b,c,d,e,f,g,h){ var sx =136+a;//start position of curve.used in moveTo(sx,sy) var sy =235+b; var cp1x=136+c;//control point 1 var cp1y=222+d; var cp2x=136+e;//control point 2 var cp2y=222+f; var endx=136+g;//end points var endy=210+h; var canvas = document.getElementById('myCanvas'); var context = canvas.getContext('2d'); // context.clearRect(0, 0,500,500); context.strokeStyle="grey"; context.lineWidth="8"; context.beginPath(); context.moveTo(sx,sy); context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,endx,endy); context.lineCap = 'round'; context.stroke(); // context.restore(); // context.save(); } }; })(); //this function provides and calculate the bend on mousemove function bend_value(){ var ref1=135;//this is ref point for hair or curve no 1 var ref2=150;//hair no 2 and so on var ref3=165; var ref4=180; var ref5=195; var ref6=210; var ref7=225; var ref8=240; var ref9=255; if(between(x2,115,270) && between(y2,205,236)) { if(x2>=135 && x2<=145){bend1=(x2-ref1)*(2.2);} if(x2<=135 && x2>=125){bend1=(x2-ref1)*(2.2);} if(x2>=150 && x2<=160){bend2=(x2-ref2)*(2.2);} if(x2<=150 && x2>=140){bend2=(x2-ref2)*(2.2);} if(x2>=165 && x2<=175){bend3=(x2-ref3)*(2.2);} if(x2<=165 && x2>=155){bend3=(x2-ref3)*(2.2);} if(x2>=180 && x2<=190){bend4=(x2-ref4)*(2.2);} if(x2<=180 && x2>=170){bend4=(x2-ref4)*(2.2);} if(x2>=195 && x2<=205){bend5=(x2-ref5)*(2.2);} if(x2<=195 && x2>=185){bend5=(x2-ref5)*(2.2);} if(x2>=210 && x2<=220){bend6=(x2-ref6)*(2.2);} if(x2<=210 && x2>=200){bend6=(x2-ref6)*(2.2);} if(x2>=225 && x2<=235){bend7=(x2-ref7)*(2.2);} if(x2<=225 && x2>=215){bend7=(x2-ref7)*(2.2);} if(x2>=240 && x2<=250){bend8=(x2-ref8)*(2.2);} if(x2<=240 && x2>=230){bend8=(x2-ref8)*(2.2);} if(x2>=255 && x2<=265){bend9=(x2-ref9)*(2.2);} if(x2<=255 && x2>=245){bend9=(x2-ref9)*(2.2);} } } function init(){//this function draws each hair/curve var canvas = document.getElementById('myCanvas'); var context = canvas.getContext('2d'); var clear=context.clearRect(0, 0,500,500); var save=context.save(); // /* console.log("bend2="+bend2) // console.log("bend3="+bend3) // console.log("bend4="+bend4) // console.log("bend5="+bend5) // console.log("bend6="+bend6) // console.log("bend7="+bend7) // console.log("bend8="+bend8) // console.log("bend9="+bend9)*/ hd1 = new hair();//hd1 stands for hair draw 1.this is an instance created for drawing hair no 1 clear; hd1.draw_hair(0,0,0,0,0,0,0+bend1/2,0);//these parameters passed to function drawhair and bend is beint retrieved from function bend_value() save; hd2 = new hair(); clear; hd2.draw_hair(15,0,15,0,15,0,15+bend2/2,0); save; hd3 = new hair(); clear; hd3.draw_hair(30,0,30,0,30,0,30+bend3/2,0); save; hd4 = new hair(); clear; hd4.draw_hair(45,0,45,0,45,0,45+bend4/2,0); save; hd5 = new hair(); clear; hd5.draw_hair(60,0,60,0,60,0,60+bend5/2,0); save; } window.onload = function() { init(); disableSelection(document.body) } function disableSelection(target){ if (typeof target.onselectstart!="undefined") //IE target.onselectstart=function(){return false} else if (typeof target.style.MozUserSelect!="undefined") //Firefox target.style.MozUserSelect="none" else //All other ie: Opera target.onmousedown=function(){return false} target.style.cursor = "default" } 
+4
source share
5 answers

Update: I am currently adjusting the code to get the requested result and commenting.

 (function() { // The code is encapsulated in a self invoking function to isolate the scope "use strict"; // The following lines creates shortcuts to the constructors of the Box2D types used var B2Vec2 = Box2D.Common.Math.b2Vec2, B2BodyDef = Box2D.Dynamics.b2BodyDef, B2Body = Box2D.Dynamics.b2Body, B2FixtureDef = Box2D.Dynamics.b2FixtureDef, B2Fixture = Box2D.Dynamics.b2Fixture, B2World = Box2D.Dynamics.b2World, B2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, B2RevoluteJoint = Box2D.Dynamics.Joints.b2RevoluteJoint, B2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef; // This makes sure that there is a method to request a callback to update the graphics for next frame window.requestAnimationFrame = window.requestAnimationFrame || // According to the standard window.mozRequestAnimationFrame || // For mozilla window.webkitRequestAnimationFrame || // For webkit window.msRequestAnimationFrame || // For ie function (f) { window.setTimeout(function () { f(Date.now()); }, 1000/60); }; // If everthing else fails var world = new B2World(new B2Vec2(0, -10), true), // Create a world with gravity physicalObjects = [], // Maintain a list of the simulated objects windInput = 0, // The input for the wind in the current frame wind = 0, // The current wind (smoothing the input values + randomness) STRAW_COUNT = 10, // Number of straws GRASS_RESET_SPEED = 2, // How quick should the straw reset to its target angle POWER_MOUSE_WIND = 120, // How much does the mouse affect the wind POWER_RANDOM_WIND = 180; // How much does the randomness affect the wind // GrassPart is a prototype for a piece of a straw. It has the following properties // position: the position of the piece // density: the density of the piece // target: the target angle of the piece // statik: a boolean stating if the piece is static (ie does not move) function GrassPart(position, density, target, statik) { this.width = 0.05; this.height = 0.5; this.target = target; // To create a physical body in Box2D you have to setup a body definition // and create at least one fixture. var bdef = new B2BodyDef(), fdef = new B2FixtureDef(); // In this example we specify if the body is static or not (the grass roots // has to be static to keep the straw in its position), and its original // position. bdef.type = statik? B2Body.b2_staticBody : B2Body.b2_dynamicBody; bdef.position.SetV(position); // The fixture of the piece is a box with a given density. The negative group index // makes sure that the straws does not collide. fdef.shape = new B2PolygonShape(); fdef.shape.SetAsBox(this.width/2, this.height/2); fdef.density = density; fdef.filter.groupIndex = -1; // The body and fixture is created and added to the world this.body = world.CreateBody(bdef); this.body.CreateFixture(fdef); } // This method is called for every frame of animation. It strives to reset the original // angle of the straw (the joint). The time parameter is unused here but contains the // current time. GrassPart.prototype.update = function (time) { if (this.joint) { this.joint.SetMotorSpeed(GRASS_RESET_SPEED*(this.target - this.joint.GetJointAngle())); } }; // The link method is used to link the pieces of the straw together using a joint // other: the piece to link to // torque: the strength of the joint (stiffness) GrassPart.prototype.link = function(other, torque) { // This is all Box2D specific. Look it up in the manual. var jdef = new B2RevoluteJointDef(); var p = this.body.GetWorldPoint(new B2Vec2(0, 0.5)); // Get the world coordinates of where the joint jdef.Initialize(this.body, other.body, p); jdef.maxMotorTorque = torque; jdef.motorSpeed = 0; jdef.enableMotor = true; // Add the joint to the world this.joint = world.CreateJoint(jdef); }; // A prototype for a straw of grass // position: the position of the bottom of the root of the straw function Grass(position) { var pos = new B2Vec2(position.x, position.y); var angle = 1.2*Math.random() - 0.6; // Randomize the target angle // Create three pieces, the static root and to more, and place them in line. // The second parameter is the stiffness of the joints. It controls how the straw bends. // The third is the target angle and different angles are specified for the pieces. this.g1 = new GrassPart(pos, 1, angle/4, true); // This is the static root pos.Add(new B2Vec2(0, 1)); this.g2 = new GrassPart(pos, 0.75, angle); pos.Add(new B2Vec2(0, 1)); this.g3 = new GrassPart(pos, 0.5); // Link the pieces into a straw this.g1.link(this.g2, 20); this.g2.link(this.g3, 3); // Add the pieces to the list of simulate objects physicalObjects.push(this.g1); physicalObjects.push(this.g2); physicalObjects.push(this.g3); } Grass.prototype.draw = function (context) { var p = new B2Vec2(0, 0.5); var p1 = this.g1.body.GetWorldPoint(p); var p2 = this.g2.body.GetWorldPoint(p); var p3 = this.g3.body.GetWorldPoint(p); context.strokeStyle = 'grey'; context.lineWidth = 0.4; context.lineCap = 'round'; context.beginPath(); context.moveTo(p1.x, p1.y); context.quadraticCurveTo(p2.x, p2.y, p3.x, p3.y); context.stroke(); }; var lastX, grass = [], context = document.getElementById('canvas').getContext('2d'); function updateGraphics(time) { window.requestAnimationFrame(updateGraphics); wind = 0.95*wind + 0.05*(POWER_MOUSE_WIND*windInput + POWER_RANDOM_WIND*Math.random() - POWER_RANDOM_WIND/2); windInput = 0; world.SetGravity(new B2Vec2(wind, -10)); physicalObjects.forEach(function(obj) { if (obj.update) obj.update(time); }); world.Step(1/60, 8, 3); world.ClearForces(); context.clearRect(0, 0, context.canvas.width, context.canvas.height); context.save(); context.translate(context.canvas.width/2, context.canvas.height/2); context.scale(context.canvas.width/20, -context.canvas.width/20); grass.forEach(function (o) { o.draw(context); }); context.restore(); } document.getElementsByTagName('body')[0].addEventListener("mousemove", function (e) { windInput = Math.abs(lastX - ex) < 200? 0.2*(ex - lastX) : 0; lastX = ex; }); var W = 8; for (var i = 0; i < STRAW_COUNT; i++) { grass.push(new Grass(new B2Vec2(W*(i/(STRAW_COUNT-1))-W/2, -1))); } window.requestAnimationFrame(updateGraphics); })(); 
+4
source

Grass Swing Algorithm

UPDATE

I did an abbreviated update to better fit what I consider your requirements. To use the mouse, you simply calculate the angle between the mouse point and the warp root and use this for the new angle in the update.

I have included a simple mouse-sensitive approach that makes the stamps β€œpoint” to the mouse, but you can add random angles like delta, etc. to this. All you need is as indicated in the code - adjust if necessary.

New violin (based on the previous one with a few changes):
http://jsfiddle.net/AbdiasSoftware/yEwGc/

enter image description here

Grass simulation snaphot - 150 strains Image depicting 150 strains simulated.

Demo version of Grass model:
http://jsfiddle.net/AbdiasSoftware/5z89V/

This will create a nice realistic grass field. The demo has 70 herbs (works best in Chrome or just reduces the amount for Firefox).

The code is pretty simple. It consists of the main object ( grassObj ), which contains its geometry, as well as functions for calculating angles, segments, movements, etc. I will show it in detail below.

First, some elements that are available globally by function:

 var numOfGrass = 70, /// number of grass strains grass, /// get canvas context ctx = canvas.getContext('2d'), w = canvas.width, h = canvas.height, /// we use an animated image for the background /// The image also clears the canvas for each loop call /// I rendered the clouds in a 3D software. img = document.createElement('img'), ix = 0, /// background image position iw = -1; /// used for with and initial for flag /// load background image, use it whenever it ready img.onload = function() {iw = this.width} img.src = 'http://i.imgur.com/zzjtzG7.jpg'; 

Heart - grassObj

The main object mentioned above is grassObj :

 function grassObj(x, y, seg1, seg2, maxAngle) { /// exposed properties we need for rendering this.x = x; /// bottom position of grass this.y = y; this.seg1 = seg1; /// segments of grass this.seg2 = seg2; this.gradient = getGradient(Math.random() * 50 + 50, 100 * Math.random() + 170); this.currentAngle; ///current angle that will be rendered /// internals used for calculating new angle, goal, difference and speed var counter, /// counter between 0-1 for ease-in/out delta, /// random steps in the direction goal rel. c.angle. angle, /// current angle, does not change until goal is reached diff, /// diff between goal and angle goal = getAngle(); /// internal: returns an angel between 0 and maxAngle function getAngle() { return maxAngle * Math.random(); } /// ease in-out function function easeInOut(t) { return t < 0.5 ? 4 * t * t * t : (t-1) * (2 * t - 2) * (2 * t - 2) + 1; } /// sets a new goal for grass to move to. Does the main calculations function newGoal() { angle = goal; /// set goal as new angle when reached this.currentAngle = angle; goal = getAngle(); /// get new goal diff = goal - angle; /// calc diff counter = 0; /// reset counter delta = (4 * Math.random() + 1) / 100; } /// creates a gradient for this grass to increase realism function getGradient(min, max) { var g = ctx.createLinearGradient(0, 0, 0, h); g.addColorStop(1, 'rgb(0,' + parseInt(min) + ', 0)'); g.addColorStop(0, 'rgb(0,' + parseInt(max) + ', 0)'); return g; } /// this is called from animation loop. Counts and keeps tracks of /// current position and calls new goal when current goal is reached this.update = function() { /// count from 0 to 1 with random delta value counter += delta; /// if counter passes 1 then goal is reached -> get new goal if (counter > 1) { newGoal(); return; } /// ease in/out function var t = easeInOut(counter); /// update current angle for render this.currentAngle = angle + t * diff; } /// init newGoal(); return this; } 

Grass generator

We call makeGrass to generate grass in random positions, random heights and random segments. The function is called with the amount of grass to render, the width and height of the canvas to fill, and the variable change in percent (0 - 1 float).

The only grass consists of only four points. The two midpoints are distributed approximately 1/3 and 2/3 of the total height with a slight change in the gap. The points are smoother when rendering, using a cardinal spline with full tension to make the grass look smooth.

 function makeGrass(numOfGrass, width, height, hVariation) { /// setup variables var x, y, seg1, seg2, angle, hf = height * hVariation, /// get variation i = 0, grass = []; /// array to hold the grass /// generate grass for(; i < numOfGrass; i++) { x = width * Math.random(); /// random x position y = height - hf * Math.random(); /// random height /// break grass into 3 segments with random variation seg1 = y / 3 + y * hVariation * Math.random() * 0.1; seg2 = (y / 3 * 2) + y * hVariation * Math.random() * 0.1; grass.push(new grassObj(x, y, seg1, seg2, 15 * Math.random() + 50)); } return grass; } 

Render

The render function simply projects objects and updates the current geometry:

 function renderGrass(ctx, grass) { /// local vars for animation var len = grass.length, i = 0, gr, pos, diff, pts, x, y; /// renders background when loaded if (iw > -1) { ctx.drawImage(img, ix--, 0); if (ix < -w) { ctx.drawImage(img, ix + iw, 0); } if (ix <= -iw) ix = 0; } else { ctx.clearRect(0, 0, w, h); } /// loops through the grass object and renders current state for(; gr = grass[i]; i++) { x = gr.x; y = gr.y; ctx.beginPath(); /// calculates the end-point based on length and angle /// Angle is limited [0, 60] which we add 225 deg. to get /// it upwards. Alter 225 to make grass lean more to a side. pos = lineToAngle(ctx, x, h, y, gr.currentAngle + 225); /// diff between end point and root point diff = (pos[0] - x) pts = []; /// starts at bottom, goes to top middle and then back /// down with a slight offset to make the grass pts.push(x); /// first couple at bottom pts.push(h); /// first segment 1/4 of the difference pts.push(x + (diff / 4)); pts.push(h - gr.seg1); /// second segment 2/3 of the difference pts.push(x + (diff / 3 * 2)); pts.push(h - gr.seg2); pts.push(pos[0]); /// top point pts.push(pos[1]); /// re-use previous data, but go backward down to root again /// with a slight offset pts.push(x + (diff / 3 * 2) + 10); pts.push(h - gr.seg2); pts.push(x + (diff / 4) + 12); pts.push(h - gr.seg1 + 10); pts.push(x + 15); /// end couple at bottom pts.push(h); /// smooth points (extended context function, see demo) ctx.curve(pts, 0.8, 5); ctx.closePath(); /// fill grass with its gradient ctx.fillStyle = gr.gradient; ctx.fill(); } } 

Animate

The main loop where we animate everything:

 function animate() { /// update each grass objects for(var i = 0;i < grass.length; i++) grass[i].update(); /// render them renderGrass(ctx, grass); /// loop requestAnimationFrame(animate); } 

And all this for this version.

+3
source

Heck! Late to the party ...

But ALMOST of the neat answers here - I am increasing!

Anyway, here is my idea:

enter image description here

Here's the code and script: http://jsfiddle.net/m1erickson/MJjHz/

 <!doctype html> <html> <head> <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css --> <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/ui/1.10.1/jquery-ui.js"></script> <style> body { font-family: arial; padding:15px; } canvas { border: 1px solid red;} input[type="text"]{width:35px;} </style> </head> <body> <p>Move mouse across hairs</p> <canvas height="100" width="250" id="canvas"></canvas> <script> $(function() { var canvas=document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var canvasOffset=$("#canvas").offset(); var offsetX=canvasOffset.left; var offsetY=canvasOffset.top; var cHeight=canvas.height; var showControls=false; var lastMouseX=0; // preset styling CONSTANTS var SWAY=.55; // max endpoint sway from center var C1Y=.40; // fixed Y of cp#1 var C2SWAY=.20 // max cp#2 sway from center var C2Y=.75; // fixed Y of cp#2 var YY=20; // max height of ellipse at top of hair var PIPERCENT=Math.PI/100; var hairs=[]; // create hairs var newHairX=40; var hairCount=20; for(var i=0;i<hairCount;i++){ var randomLength=50+parseInt(Math.random()*5); addHair(newHairX+(i*8),randomLength); } function addHair(x,length){ hairs.push({ x:x, length:length, left:0, right:0, top:0, s:{x:0,y:0}, c1:{x:0,y:0}, c2:{x:0,y:0}, e:{x:0,y:0}, isInMotion:false, currentX:0 }); } for(var i=0;i<hairs.length;i++){ var h=hairs[i]; setHairPointsFixed(h); setHairPointsPct(h,50); draw(h); } function setHairPointsFixed(h){ hsx = hx; hsy = cHeight; h.c1.x = hx; h.c1.y = cHeight-h.length*C1Y; h.c2.y = cHeight-h.length*C2Y; h.top = cHeight-h.length; h.left = hx-h.length*SWAY; h.right = h.x+h.length*SWAY; } function setHairPointsPct(h,pct){ // endpoint var a=Math.PI+PIPERCENT*pct; hex = hx - ((h.length*SWAY)*Math.cos(a)); hey = h.top + (YY*Math.sin(a)); // controlpoint#2 h.c2.x = hx + h.length*(C2SWAY*2*pct/100-C2SWAY); } ////////////////////////////// function handleMouseMove(e){ mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY); // draw this frame based on mouse moves ctx.clearRect(0,0,canvas.width,canvas.height); for(var i=0;i<hairs.length;i++){ hairMoves(hairs[i],mouseX,mouseY); } lastMouseX=mouseX; } $("#canvas").mousemove(function(e){handleMouseMove(e);}); function hairMoves(h,mouseX,mouseY){ // No hair movement if not touching hair if(mouseY<cHeight-h.length-YY){ if(h.isInMotion){ h.isInMotion=false; setHairPointsPct(h,50); } draw(h); return; } // No hair movement if too deep in hair if(mouseY>h.c1.y){ draw(h); return; } // var pct=50; if(mouseX>=h.left && mouseX<=h.right){ if(h.isInMotion){ var pct=-(mouseX-h.right)/(h.right-h.left)*100; setHairPointsPct(h,pct); draw(h); }else{ // if hair is at rest // but mouse has just contacted hair // set hair in motion if( (lastMouseX<=hx && mouseX>=hx ) ||(lastMouseX>=hx && mouseX<=hx ) ){ h.isInMotion=true; var pct=-(mouseX-h.right)/(h.right-h.left)*100; } setHairPointsPct(h,pct); draw(h); } }else{ if(h.isInMotion){ h.isInMotion=false; setHairPointsPct(h,50); }; draw(h); } } function dot(pt,color){ ctx.beginPath(); ctx.arc(pt.x,pt.y,5,0,Math.PI*2,false); ctx.closePath(); ctx.fillStyle=color; ctx.fill(); } function draw(h){ ctx.beginPath(); ctx.moveTo(hsx,hsy); ctx.bezierCurveTo(h.c1.x,h.c1.y,h.c2.x,h.c2.y,hex,hey); ctx.strokeStyle="orange"; ctx.lineWidth=3; ctx.stroke(); if(showControls){ dot(hs,"green"); dot(h.c1,"red"); dot(h.c2,"blue"); dot(he,"purple"); ctx.beginPath(); ctx.rect(h.left,h.top-YY,(h.right-h.left),h.length*(1-C1Y)+YY) ctx.lineWidth=1; ctx.strokeStyle="lightgray"; ctx.stroke(); } } }); </script> </body> </html> 
+2
source

Here is a simple hair simulation that seems to be what you are looking for. The main idea is to draw a Bezier curve (in this case I use two curves to ensure the thickness of the hair). The curve will have a base, a bend point, and a tip. I set the bend point halfway up. The hair tip will rotate around the axis of the hair base in response to mouse movement.

Put this code in the script tag below the canvas element declaration.

  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 < 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 < this.points.base1.x - this.height/2 || mouseX > this.points.base1.x + this.height/2 || mouseY < this.points.base1.y - this.height || mouseY > 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 < this.points.base1.x && direction == 'right') || (mouseX > this.points.base1.x && 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 < 100; i++) { hair.push(new Hair()); } // initial draw ctx.fillRect(0,0,canvas.width,canvas.height); // clear canvas for (var i = 0; i < 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 < hair.length; i++) { hair[i].paint(e.clientX, e.clientY, e.clientX > window.prevClientX ? 'right' : 'left'); } window.prevClientX = e.clientX; } 
+1
source

Done this some time ago, may be useful to some people. Just adjust the variables at the beginning of the code with the values ​​that match your wishes:

 ... Mheight = 1; height = 33; width = 17; distance = 10; randomness = 14; angle = Math.PI / 2; ... 

Also at http://lucasm0ta.imtqy.com/JsGrass/

0
source

Source: https://habr.com/ru/post/1486840/


All Articles