Трава как сглаживающая анимация на кривой Безье?

Это то, чего я пытаюсь добиться - GRASS Animation (Желаемая анимация)

Вот где сейчас находится проект --Моя анимация волос

Это более структурированный код приведенного выше кода --Моя анимация волос(автор markE)--код markE анимация волос

ПРОБЛЕМА:--

Я могу придать движения волоскам, но анимация должна быть больше похожа на волнистую траву, на плавную. Сейчас она не очень гладкая. Что можно сделать, чтобы волосы струились более естественно. Пожалуйста, предоставьте мне небольшой образец, если это возможно!

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

ЯВАСКРИПТ

//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"
}

person HIRA THAKUR    schedule 18.06.2013    source источник
comment
Код травы, кажется, отслеживает текущую метку времени и использует ее для позиционирования материала как функции времени. Например, здесь: var inc_punta_hierba= Math.sin(time*0.0005);   -  person bfavaretto    schedule 18.06.2013
comment
Да, я согласен, но что, если я хочу анимацию при движении мыши?   -  person HIRA THAKUR    schedule 18.06.2013
comment
@bfavaretto: не могли бы вы уточнить, как это происходит при загрузке, если бы не движение мыши. Мне трудно понять часть времени !!! и часть sin и cos, которая определяет движение !!   -  person HIRA THAKUR    schedule 18.06.2013
comment
К сожалению, я также не уверен в деталях реализации. Но что именно вы ищете? Вы хотите, чтобы волосы какое-то время продолжали развеваться после того, как вы наведете на них указатель мыши?   -  person bfavaretto    schedule 18.06.2013
comment
@bfavaretto: нет, все в порядке, если волосы идут вправо, если моя мышь движется в правильном направлении, и аналогично для левого. Но анимация должна быть в виде шаблона, показанного в приведенной выше ссылке (анимация травы). Мне просто нужен образец. было бы здорово, если бы вы могли сделать эту анимацию для одной кривой Безье (или волос).   -  person HIRA THAKUR    schedule 18.06.2013
comment
@markE: я уже достаточно перепробовал. помогите мне с этим сейчас !!!   -  person HIRA THAKUR    schedule 22.06.2013
comment
Если вы можете снизить производительность, я бы предложил использовать box2d (например, code.google.com/p/box2dweb) и использовать события мыши, чтобы вызвать силы, такие как ветер, дующий на траву. Скорее всего, это выглядело бы очень красиво :)   -  person Tobias Ritzau    schedule 24.06.2013
comment
@TobiasRitzau: ага, все в порядке, я просто хочу этот ленивый изгиб ... даже на одной кривой Безье подойдет ... как это возможно???   -  person HIRA THAKUR    schedule 24.06.2013
comment
Я думаю, что вы можете установить его относительно легко, но я должен играть с ним. Я думаю несколько узлов (3-4) с соединениями, которые стремятся держать солому прямо. Рандомизируйте вес каждой соломинки и силу соединений. Добавляйте силы по мере перемещения мыши. Визуализируйте с Безье и создайте столько соломинок, сколько хотите :) Я постараюсь сделать рабочий образец позже сегодня, но не могу обещать...   -  person Tobias Ritzau    schedule 24.06.2013
comment
@TobiasRitzau: это очень мило с вашей стороны. спасибо, что рассмотрели этот вопрос, достойный вашего времени.   -  person HIRA THAKUR    schedule 24.06.2013


Ответы (5)


Обновление: в настоящее время я корректирую код для получения запрошенного результата и комментируя это.

(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 (i.e. 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 - e.x) < 200? 0.2*(e.x - lastX) : 0;
      lastX = e.x;
    });

    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);
})();
person Tobias Ritzau    schedule 25.06.2013
comment
: хорошо, нам не нужно так много волос. Если вы видите ссылку выше (моя анимация волос), есть несколько кривых Безье, которые нуждаются в ленивой траве, такой как анимация. То, что вы сделали, феноменально, но я думаю, что мы близки, но все же не там!!! Ответить - person HIRA THAKUR; 25.06.2013
comment
Мы почти у цели!!!Это великолепная анимация!!!Можем ли мы уменьшить скорость, с которой они изгибаются.Я увидел ваш код, я был ошеломлен!!!Javascript высшего уровня... - person HIRA THAKUR; 25.06.2013
comment
Конечно, вы можете уменьшить количество соломинок, и есть множество настроек, которые нужно сделать, чтобы получить именно то, что вы хотите. Использование Box2D в данном случае излишне, но оно простое в использовании и открывает массу новых возможностей :) Вот новая ссылка с извлеченными некоторыми параметрами. - person Tobias Ritzau; 25.06.2013
comment
Должно быть вполне возможно, но я не уверен на 100% в том, что вы имеете в виду :) Код можно переместить на вашу скрипку, и разместить соломинки, как у вас, легко, как и рисование в вашем стиле. Вы должны быть в состоянии сделать это легко. - person Tobias Ritzau; 25.06.2013
comment
Вы должны посетить веб-сайт моей компании. Выше приведена ссылка. Логотип моей компании включает в себя волосы и стекло. Вот где идет эта анимация. Теперь я ясно!!! Как долго вы занимаетесь javascript? - person HIRA THAKUR; 25.06.2013
comment
Да, я видел логотип. Красиво :) Это не должно быть проблемой. Вы должны иметь возможность интегрировать его, размещая волосы по своему усмотрению и переопределяя метод рисования. Это зависит от того, что вы подразумеваете под javascript. Я не работал с javascript в каком-либо продвинутом контексте, но я играл с ним время от времени в течение многих лет. Последние пару месяцев я был более активен. - person Tobias Ritzau; 25.06.2013
comment
Спасибо, чувак!!! сколько времени это заняло.. Я трачу всю свою неделю на этот эффект.. но не понимаю - person HIRA THAKUR; 25.06.2013
comment
Я потратил на это пару часов. Я взял код старой игры, чтобы с чего-то начать. Это, вероятно, стоило больше времени, чем я выиграл, так как я все равно переписал большую часть этого :) Просто пингуйте меня, если у вас есть дополнительные вопросы или когда вам есть что показать. хотелось бы увидеть результат :) - person Tobias Ritzau; 25.06.2013
comment
Конечно, я дам вам знать, если у меня возникнут проблемы !!! Большое спасибо .. я почти сдался !!! - person HIRA THAKUR; 25.06.2013
comment
извините, дело в том, что сейчас я работаю над другим проектом, и у меня нет возможности провести некоторое время с проектом волос. У меня есть только выходные, но я просмотрел ваш код.. это было похоже на инопланетный javascript.. Я никогда раньше не видел таких вещей... За исключением травы.prototype.draw, я почти ничего не понял... можете ли вы прокомментировать свой код для простоты и понимания цели. Я много настроил ваш код и несколько параметров, но в целом я не поймите ваш код ... может быть, мне нужно повысить свой уровень сейчас !!! - person HIRA THAKUR; 26.06.2013
comment
Я вернусь к этому в субботу !!! посмотрите и другой ответ, даже это круто - person HIRA THAKUR; 26.06.2013
comment
Дайте мне награду, и я помогу вам пройти через это :) Я думаю, что это код Box2D, который вам все испортил. - person Tobias Ritzau; 26.06.2013
comment
Конечно, я дам его тебе... если ты увидишь общую производительность!! твоя анимация очень похожа на то, что мы желаем!!! - person HIRA THAKUR; 26.06.2013
comment
Ну-ну-ну... завтра в субботу, и, как я уже сказал, я вернусь к этому... некоторые отступы и преобразования кода все еще остаются с вашей стороны... можете ли вы дать такую ​​же анимацию тому, что я сделал до сих пор--- ›jsfiddle.net/jayeshjain24/UmSss/2 - person HIRA THAKUR; 28.06.2013
comment
Обновил ссылку в чате. Я думаю, что это должно быть сделано. Пожалуйста, укажите, что вы хотите уточнить. Я тоже почищу пост, когда вы будете довольны. - person Tobias Ritzau; 28.06.2013

Алгоритм развевающейся травы

ОБНОВЛЕНИЕ

Я сделал сокращенное обновление, чтобы лучше соответствовать вашим требованиям. Чтобы использовать мышь, вы просто вычисляете угол между точкой мыши и корнем деформации и используете его для нового угла в обновлении.

Я внедрил простой подход, чувствительный к движению мыши, который заставляет деформации «указывать» на мышь, но вы можете добавить к этому случайные углы, такие как дельты и так далее. Все, что вам нужно, указано в коде — при необходимости отрегулируйте.

Новая скрипта (основана на предыдущей с некоторыми изменениями):
http://jsfiddle.net/AbdiasSoftware/yEwGc/

введите здесь описание изображения

Моделирование травы — 150 штаммовИзображение, на котором моделируются 150 штаммов.

Демонстрация моделирования травы:
http://jsfiddle.net/AbdiasSoftware/5z89V/

Это создаст красивое реалистично выглядящее травяное поле. В демо-версии отображается 70 трав (лучше всего работает в Chrome или просто уменьшите число для Firefox).

Код довольно прост. Он состоит из основного объекта (grassObj), который содержит его геометрию, а также функции для расчета углов, сегментов, перемещений и т.д. Я покажу это подробно ниже.

Сначала несколько инициализаций, к которым глобально обращаются функции:

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's ready
    img.onload = function() {iw = this.width}
    img.src = 'http://i.imgur.com/zzjtzG7.jpg';

Сердце - траваОбж

Основным объектом, как упоминалось выше, является 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;
}

Генератор травы

Мы вызываем makeGrass для генерации травы в случайных положениях, случайных высотах и ​​со случайными сегментами. Функция вызывается с указанием количества травы для рендеринга, ширины и высоты холста для заполнения и переменной вариации в процентах (0–1 с плавающей запятой).

Одиночная трава состоит всего из четырех точек. Две средние точки разбросаны примерно на 1/3 и 2/3 общей высоты с небольшими вариациями для разрыва рисунка. Точки при рендеринге становятся более гладкими с использованием кардинального сплайна с полным натяжением, чтобы трава выглядела гладкой.

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;
}

Оказывать

Функция рендеринга просто перебирает объекты и обновляет текущую геометрию:

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();
    }
}

Анимировать

Основной цикл, в котором мы все анимируем:

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);
}

И это все, что нужно для этой версии.

person Community    schedule 24.06.2013
comment
Спасибо, Кен. Я ценю ваши усилия. Я видел ваш код, и он был прекрасен. Но я хочу анимацию на 10 простых кривых Безье (также называемых волосами). Мне не нужен этот причудливый фон, так много трав. . ЕСЛИ вы увидите ссылку --- Моя анимация волос выше, вы поймете, о чем я говорю. Вы увидите, что есть очень короткие волосы, также известные как кривые Безье. Я хочу, чтобы трава была похожа на ленивый изгиб, который вы видите в самой первой ссылке на мой вопрос об этих волосах. В любом случае, я думаю, что ваш код действительно превосходен. Спасибо, приятель !!! - person ; 24.06.2013
comment
@jayeshjain Не стесняйтесь изменять код. Вы можете просто настроить параметры в соответствии с вашими требованиями, например, установить 10 штаммов и уменьшить высоту. Я просто немного раздвинул конверт, чтобы увидеть его потенциал, а фон был просто для удовольствия;) - person ; 24.06.2013
comment
@Ken-AbdiasSoftware: хорошо, я попробую отредактировать. Если я не получу желаемых результатов, я дам вам знать !!! Кроме того, мой проект полностью основан на событиях движения мыши, что делает его головной болью !!! - person HIRA THAKUR; 24.06.2013
comment
@MESSIAH Я обновил ответ с несколькими изменениями. Все, что вам нужно, есть. Измените по мере необходимости. - person ; 26.06.2013
comment
@Ken-AbdiasSoftware: спасибо, Кен, я немного изменю ваш код, вы поняли.!!! спасибо за ответ и извините за боль!!! - person HIRA THAKUR; 26.06.2013
comment
@MESSIAH нет боли нет выгоды ;) - person ; 26.06.2013
comment
@Ken-AbdiasSoftware: +1, хороший эффект и особенно хороший код ... стоит два плюс один, если бы я мог их дать! - person markE; 26.06.2013

Черт! Опаздываю на вечеринку...

Но МНОГО аккуратных ответов здесь - я голосую за все!

В любом случае, вот моя идея:

введите здесь описание изображения

Вот код и скрипт: 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){
            h.s.x   = h.x;
            h.s.y   = cHeight;
            h.c1.x  = h.x;
            h.c1.y  = cHeight-h.length*C1Y;
            h.c2.y  = cHeight-h.length*C2Y;
            h.top   = cHeight-h.length;
            h.left  = h.x-h.length*SWAY;
            h.right = h.x+h.length*SWAY;
        }


        function setHairPointsPct(h,pct){
            // endpoint
            var a=Math.PI+PIPERCENT*pct;
            h.e.x  = h.x - ((h.length*SWAY)*Math.cos(a));
            h.e.y  = h.top + (YY*Math.sin(a));
            // controlpoint#2
            h.c2.x = h.x + 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<=h.x && mouseX>=h.x )
                      ||(lastMouseX>=h.x && mouseX<=h.x )
                  ){
                      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(h.s.x,h.s.y);
            ctx.bezierCurveTo(h.c1.x,h.c1.y,h.c2.x,h.c2.y,h.e.x,h.e.y);
            ctx.strokeStyle="orange";
            ctx.lineWidth=3;
            ctx.stroke();

            if(showControls){
                dot(h.s,"green");
                dot(h.c1,"red");
                dot(h.c2,"blue");
                dot(h.e,"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>
person markE    schedule 26.06.2013
comment
:спасибо за ответ!!!но изгиб немного отличается от желаемого!!!можно ли это изменить и сделать несколько ленивых движений?? - person HIRA THAKUR; 26.06.2013
comment
Ааа... Я вспомнил ваш предыдущий вопрос, где вы хотели, чтобы волосы реагировали на движения мыши. Конечно, вы можете просто подключить requestAnimationFrame для имитации движений мыши. Вот как: jsfiddle.net/m1erickson/R8ba5 - person markE; 26.06.2013
comment
Мне нравится ваша техника здесь для прогиба под курсором мыши. - person Jordan Gray; 27.06.2013

Вот простая симуляция волос, которая, кажется, то, что вы ищете. Основная идея состоит в том, чтобы нарисовать кривую Безье (в данном случае я использую две кривые, чтобы придать толщину волосам). Кривая будет иметь основание, точку изгиба и вершину. Я установил точку изгиба посередине волос. Кончик волоса будет вращаться вокруг оси основания волоса в ответ на движение мыши.

Поместите этот код в тег script под объявлением элемента canvas.

    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;
    }
person jSource    schedule 24.06.2013
comment
Я ценю ваши усилия, но это далеко не то, что я хочу. Вы должны видеть обе ссылки. Я хочу, чтобы эта ленивая трава, например, изгибалась до 5 или 10 волосков. Спасибо за ваш ответ - person HIRA THAKUR; 24.06.2013

Сделано это некоторое время назад, может быть полезно для некоторых людей. Просто настройте переменные в начале кода на значения, соответствующие вашим пожеланиям:

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

Также на http://lucasm0ta.github.io/JsGrass/

person Lucas Mota    schedule 25.02.2016