用p5.js实现一个小动画——故宫橘猫赏秋图

用p5.js实现一个小动画——故宫橘猫赏秋图

互动媒体第二次作业要求我们手绘一幅动画,再用代码实现出动画。由于时间原因,手绘并没有画动画,而是以插画的形式画了一张,然后p5实现了动画。
这里先放效果图:

板绘插图

有参考网上故宫图片

码绘效果图

在这里插入图片描述
这里强烈建议直接运行代码!!!gif丢帧!!!可怜我的渐变啊啊啊啊!!!
下面附上完整代码:

var Width=600;
var Height=700;
var pixel=1;

var Y_AXIS = 1;
var X_AXIS = 2;

var skyHeight=190;
var wall_Width=600;
var wall_Height=300;
var wuyan_width=120;
var wuyan_height=20;
var quad_width=70;
var quad_height=30;
var center_x=500;
var center_y=115;
var cat_scale=111;

var easing=1; 

var Time;

//face_color=color(180,180,150,0.5*255);
function setup() {
    createCanvas(Width,Height); 
  }

  function draw() { 
    
    frameRate(5);
    drawwall();  
    drawsky();
    push();
    translate(10,-5);
    YinxingTree();
    pop();

    draw_wallshadow();

    if(center_x<-10)
      center_x=650;
    center_x-=15*easing;
    drawcat(cat_scale,center_x,center_y);

    translate(10,-25);
    noStroke();
    fill(30);
    rect(Width-10,0,200,Height);

    push();
    YinxingTree();
    pop();

  }

  function drawcat(cat_scale,center_x,center_y)
  {
    stroke(200,200,240);
    noStroke();
    //肚子
    pos1_x=center_x-(cat_scale)/3;
    pos1_y=center_y+(cat_scale)*2/5-5;

    pos2_x=center_x+(cat_scale*1/3);
    pos2_y=center_y+(cat_scale)*2/5;
  
    //前体
    pos3_x=pos1_x-(cat_scale/5);
    pos3_y=center_y+(cat_scale)*2/5;

    pos4_x=pos1_x-(cat_scale/8);
    pos4_y=center_y+(cat_scale)/15;

    pos5_x=pos4_x-(cat_scale/8);
    pos5_y=pos4_y-(cat_scale)/20;

    //头
    pos6_x=pos5_x-(cat_scale/4);
    pos6_y=pos5_y-(cat_scale)/6;

    pos7_x=pos5_x-(cat_scale/6);
    pos7_y=pos5_y-(cat_scale)/30;

    pos8_x=pos5_x-(cat_scale)*3/8;
    pos8_y=pos5_y+(cat_scale)/8;

    pos9_x=pos8_x+(cat_scale)/5;
    pos9_y=pos8_y+(cat_scale)/5;
    
    //屁股
    pos10_x=pos2_x-(cat_scale/4)*0;
    pos10_y=pos2_y-(cat_scale)*1/3;

    pos11_x=pos10_x+(cat_scale*1/8);
    pos11_y=pos10_y+(cat_scale)/10;


    fill(220,200,180);

    triangle(center_x,center_y,pos1_x,pos1_y,pos2_x,pos2_y);
    triangle(center_x,center_y,pos1_x,pos1_y,pos3_x,pos3_y);
    fill(150,70,10);
    triangle(center_x,center_y,pos3_x,pos3_y,pos4_x,pos4_y);
    triangle(pos3_x,pos3_y,pos4_x,pos4_y,pos5_x,pos5_y);
    triangle(pos3_x,pos3_y,pos5_x,pos5_y,pos6_x,pos6_y);
    
    fill(150,70,10);
    triangle(pos3_x,pos3_y,pos7_x,pos7_y,pos8_x,pos8_y);
    fill(180,100,10);
    triangle(pos8_x,pos8_y,pos9_x,pos9_y,pos5_x,pos5_y);

    fill(150,70,10);
    triangle(center_x,center_y,pos2_x,pos2_y,pos10_x,pos10_y);
    triangle(pos2_x,pos2_y,pos10_x,pos10_y,pos11_x,pos11_y);

    fill(180);
    feetControl(pos1_x-6,pos1_y);
    feetControl(pos2_x-4,pos2_y);

    noFill();
    weiba(pos11_x,pos11_y);
  }

  function weiba(x,y)
  {
    push();
    strokeWeight(10);
    stroke(150,70,10);
    x1=x-20;
    y1=y;

    x2=x+20;
    y2=y-20;

    x3=x+25;
    y3=y+5;

    x4=x+55;
    y4=y-20;

    bezier(x1,y1,x2,y2,x3,y3,x4,y4);
    noStroke();
    pop();
  }

  function feetControl(x,y)
  {

    if(x%2==0)
    {
      rect(x-(cat_scale)/10,y-8,(cat_scale)/10,(cat_scale)*1/3+8);    
    }
    else
    {
      quad(x,y-10,
        x-(cat_scale)/10,y-10,
        x-(cat_scale)/10+(cat_scale/10),y+(cat_scale)*1/3,
        x+(cat_scale/10),y+(cat_scale)*1/3);

      quad(x,y-15,
        x-(cat_scale)/10,y-15,
        x-(cat_scale)/10-(cat_scale/5),y+(cat_scale)*1/3,
        x-(cat_scale/5),y+(cat_scale)*1/3);
    }
  }

  function segment(trans_x, trans_y, a,segLength) {
    push();
    translate(trans_x, trans_y);
    rotate(a);
    rect();
    pop();
  }

  function draw_wallshadow()
  {
    noStroke();
    var c1=color(160,10,0);
    var c2=color(80,10,80);
    setGradient(0,600,Width,150,c1,c2,1);

    noStroke();
    fill(160,10,0);
    for(var i=0;i<Width;i++)
    {
      arc(i,600,50,15,PI,0);
      i=i+80;
    }
  }

  function drawwall()
  {
    noStroke();
    fill(100,10,0);
    rect(0, 0, Width, Height);

    fill(190,70,20);
    rect(0, Height-wall_Height, wall_Width, wall_Height);
    
    drawWuYan1();   
    drawWuYan2(); 
    drawWuYan3();
    drawWuYan4();
  }

  function drawWuYan1()
  {
    stroke(20);
    fill(190,100,10);
    for(var i=0;i<Width;i++)
    {
      rect(i-5,wall_Height+70,wuyan_width,wuyan_height);
      i=i+wuyan_width;
    } 
  }

  function drawWuYan2()
  {
    var cwu2_1=color(50,120,30);
    var cwu2_2=color(60,10,0);

    for(var j=0;j<Width+80;j++)
    {
      setGradient(j-65,wall_Height+35,
        wuyan_width,wuyan_height+10,
        cwu2_1,cwu2_2,1);
        stroke(180,130,20);
      rect(j-65,wall_Height+36,
          wuyan_width,wuyan_height+10);
      j=j+wuyan_width;
    } 

    var cwu3_1=color(10,20,10);
    var cwu3_2=color(80,100,20);
    fill(50,120,30);
    setGradient(0,wall_Height-15,
      Width,50,cwu3_1,cwu3_2,1);
  }

  function drawWuYan3()
  {
    noStroke();
    fill(190,150,90);
    for(var k=0;k<Width;k++)
    {
      rect(k,skyHeight,wuyan_width,10);
      k=k+wuyan_width;
    }

    fill(190,100,10);
    rect(0,skyHeight+15,Width,12);
    fill(190,110,30);
    rect(0,skyHeight+35,Width,35);
  }

  function drawPIdwon(x_trans)
  {
    stroke(90,50,50);
    push();
    translate(x_trans, skyHeight+100);
    rotate(0.0);
    fill(140,100,50);
    arc(0, 0, quad_width, quad_width-15, 0, PI);
    pop();
  }

  function drawPIdwon_shadow(x_trans,shadow)
  {
    noStroke();
    push();
    translate(x_trans, skyHeight+100);
    rotate(0.0);
    fill(10,20,10);
    arc(0, 0, quad_width+shadow, quad_width+shadow, 0, PI);
    pop();
  }

  function drawquad(i,j,x_trans)
  {
    var c1=color(90,50,50);
    var c2=color(180,90,50);
    setGradient(x_trans-(quad_width/2)+i, 
    skyHeight+93-j,
    quad_width,5,c1,c2,2);
  }


function drawCicle(x_trans,angle,c1,c2,c3,i)
{
  push();
  noStroke();
  fill(c1,c2,c3);
  translate(x_trans-i+7,skyHeight+70+i*3);
  rotate(angle);
  arc(0,0,50,50, 0, PI/2);
  pop();
}

function drawCicle_all(x_trans)
{
  for(var i=0;i<8;i++)
  {
    drawCicle(x_trans+quad_width-8,24.5,100,10,10,i);
    drawCicle(x_trans+quad_width-8,-2.2,130,110,90,i);
    drawCicle(x_trans+quad_width-8,1,70,20,10,i);
    drawCicle(x_trans+quad_width-8,-3.5,200,160,80,i);
  }
  stroke(50,10,10);
  fill(140,100,50);
  ellipse(x_trans+60,skyHeight+95,50,50);
  fill(80,60,20);
  ellipse(x_trans+60,skyHeight+95,35,35);
}

function drawWuYan4()
{
    for(var x_trans=50;x_trans<Width;x_trans++)
    {
      drawPIdwon_shadow(x_trans+10,10);
      drawPIdwon(x_trans);
      for(var i=0;i<5;i++)
      {
        yp=i*5;
        drawquad(i,yp,x_trans);
      }
      drawCicle_all(x_trans);
      x_trans=x_trans+120;
    }

  
    
}

function YinxingTree()
{
  push();
  drawtree(220,180,0,-20,20,random(0.6));
  drawtree(120,60,0,-100,100,random(0.01));
  drawtree(120,60,0,-50,160,random(0.01));
  drawtree(180,160,0,40,160,random(0.05));
  drawtree(200,100,0,-20,100,random(1));
  drawtree(200,160,0,0,120,random(0.5));
  drawtree(220,160,0,55,160,random(0.1));
  drawtree(240,200,0,50,100,random(0.3));
  drawtree(240,200,0,50,180,random(0.3));
  drawtree(240,200,0,80,190,random(1));
  drawtree(220,180,0,-50,80,random(0.1));
  translate(150,90);
  drawtree(220,180,0,-50,150,random(0.5));
  translate(-100,-150);
  drawtree(240,200,120,-100,100,random(0.01));
  pop();
}

function drawtree(c1,c2,c3,pos_x,pos_y,pos_angle)
{
  push();
  rotate(pos_angle);
  var trans_x;
  var trans_y;
  var trans_angle;

  fill(c1,c2,c3);
  for(var i=0;i<20;i++)
  {
    trans_x=random(50);
    trans_y=random(20);
    trans_angle=random(-0.5);
    push();
    translate(trans_x,trans_y);
    rotate(trans_angle);
    drawYinXing(pos_x,pos_y);
    pop();
  }
  pop();
  
}

  function drawYinXing(pos_x,pos_y)
  {
    stroke(200,150,60);
    push();
    translate(pos_x, pos_y);
    rotate(0.0);
    arc(0, 0, 30, 30, 0, PI/2);
    pop();
  }

  function drawsky()
  {
    var c1 = color(90,150,205);
    var c2 = color(190,200,220);
    noStroke();
    setGradient(0, 0, Width, skyHeight,c1,c2,1);
  }

  function setGradient(x, y, w, h, c1, c2,axis) 
  {
    noFill();
    if (axis == Y_AXIS) {  // Top to bottom gradient
      for (var i = y; i <= y+h; i++) {
        var inter = map(i, y, y+h, 0, 1);
        var c = lerpColor(c1, c2, inter);
        stroke(c);
        line(x, i, x+w, i);
      }
    }  
    else if (axis == X_AXIS) {  // Left to right gradient
      for (var k = x; k <= x+w; k++) {
        var interk = map(k, x, x+w, 0, 1);
        var ck = lerpColor(c1, c2, interk);
        stroke(ck);
        line(k, y, k, y+h);
      }
    }
  }
代码结构解析

1.背景:
其实画背景还挺简单的,基本物体就是红墙,屋檐,银杏树,天空。
天空是渐变的,用了一个函数,p5官网里面也有:

function drawsky()
  {
    var c1 = color(90,150,205);
    var c2 = color(190,200,220);
    noStroke();
    setGradient(0, 0, Width, skyHeight,c1,c2,1);
  }

  function setGradient(x, y, w, h, c1, c2,axis) 
  {
    noFill();
    if (axis == Y_AXIS) {  // Top to bottom gradient
      for (var i = y; i <= y+h; i++) {
        var inter = map(i, y, y+h, 0, 1);
        var c = lerpColor(c1, c2, inter);
        stroke(c);
        line(x, i, x+w, i);
      }
    }  
    else if (axis == X_AXIS) {  // Left to right gradient
      for (var k = x; k <= x+w; k++) {
        var interk = map(k, x, x+w, 0, 1);
        var ck = lerpColor(c1, c2, interk);
        stroke(ck);
        line(k, y, k, y+h);
      }
    }
  }

红墙就不细说了,直接看屋檐,屋檐还稍微有点东西。观察故宫屋檐结构之后发现,故宫这样的建筑简直太有规律可循了!你只要生成一个基本元,接下来的就只用循环生成就可以。我们主要来看看圆木那一块怎么实现。
圆木那里其实还挺麻烦,主要是有光的影响,圆木被分为三个面:受光面,反光面,阴影面,直接用一个圆肯定解决不了,我想了一个办法,用三个扇形就可以区分三个面。
具体代码:

function drawCicle(x_trans,angle,c1,c2,c3,i)
{
  push();
  noStroke();
  fill(c1,c2,c3);
  translate(x_trans-i+7,skyHeight+70+i*3);
  rotate(angle);
  arc(0,0,50,50, 0, PI/2);
  pop();
}

function drawCicle_all(x_trans)
{
  for(var i=0;i<8;i++)
  {
    drawCicle(x_trans+quad_width-8,24.5,100,10,10,i);
    drawCicle(x_trans+quad_width-8,-2.2,130,110,90,i);
    drawCicle(x_trans+quad_width-8,1,70,20,10,i);
    drawCicle(x_trans+quad_width-8,-3.5,200,160,80,i);
  }
  stroke(50,10,10);
  fill(140,100,50);
  ellipse(x_trans+60,skyHeight+95,50,50);
  fill(80,60,20);
  ellipse(x_trans+60,skyHeight+95,35,35);
}

还有瓦片上的阴影,也用了渐变过渡,这里就不贴代码了。

银杏树
一开始对银杏树没什么头绪,观察了好几棵学校里的银杏,在大风刮过之时,金黄树叶在风中颤抖摇晃,我突然有了灵感——色块堆积。我可以不用准准确确的画出这棵树长啥样,我只需要保证它在运动中是符合这棵树的逻辑的,那么这棵树就是成功的。
下面贴上代码:

function YinxingTree()
{
  push();
  drawtree(220,180,0,-20,20,random(0.6));
  drawtree(120,60,0,-100,100,random(0.01));
  drawtree(120,60,0,-50,160,random(0.01));
  drawtree(180,160,0,40,160,random(0.05));
  drawtree(200,100,0,-20,100,random(1));
  drawtree(200,160,0,0,120,random(0.5));
  drawtree(220,160,0,55,160,random(0.1));
  drawtree(240,200,0,50,100,random(0.3));
  drawtree(240,200,0,50,180,random(0.3));
  drawtree(240,200,0,80,190,random(1));
  drawtree(220,180,0,-50,80,random(0.1));
  translate(150,90);
  drawtree(220,180,0,-50,150,random(0.5));
  translate(-100,-150);
  drawtree(240,200,120,-100,100,random(0.01));
  pop();
}

function drawtree(c1,c2,c3,pos_x,pos_y,pos_angle)
{
  push();
  rotate(pos_angle);
  var trans_x;
  var trans_y;
  var trans_angle;

  fill(c1,c2,c3);
  for(var i=0;i<20;i++)
  {
    trans_x=random(50);
    trans_y=random(20);
    trans_angle=random(-0.5);
    push();
    translate(trans_x,trans_y);
    rotate(trans_angle);
    drawYinXing(pos_x,pos_y);
    pop();
  }
  pop();
  
}

  function drawYinXing(pos_x,pos_y)
  {
    stroke(200,150,60);
    push();
    translate(pos_x, pos_y);
    rotate(0.0);
    arc(0, 0, 30, 30, 0, PI/2);
    pop();
  }

大量使用radom可以让这棵树更自然。

2.动画主角——猫
这里我先对猫进行了一些处理——低多边形处理。
吸取了第一个实验的教训,这次我先设置了一个中心点,然后在根据这个点扩充出有关猫的肢干总共12个点,然后画三角形,形成一个没有四肢,没有尾巴的橘猫。
在这里插入图片描述
尾巴用了贝塞尔曲线,坐标也跟中心点关联。
猫的四肢是运动视觉的关键!!!动画之所以能动是因为有承上启下的连续性动作。猫行走从侧面看过去就是两腿相互交叉变换。所以在写动画逻辑之前你需要先画出关键帧状态。
关键帧状态确定了就可开始着手动画逻辑:首先视觉上我们先要营造出猫在原地踏步的感觉。我们有两个关键帧状态,所以可以运用模运算,在运动的中心坐标基础上模2,结果对应两个状态。
附上代码:

function feetControl(x,y)
  {

    if(x%2==0)
    {
      rect(x-(cat_scale)/10,y-8,(cat_scale)/10,(cat_scale)*1/3+8);    
    }
    else
    {
      quad(x,y-10,
        x-(cat_scale)/10,y-10,
        x-(cat_scale)/10+(cat_scale/10),y+(cat_scale)*1/3,
        x+(cat_scale/10),y+(cat_scale)*1/3);

      quad(x,y-15,
        x-(cat_scale)/10,y-15,
        x-(cat_scale)/10-(cat_scale/5),y+(cat_scale)*1/3,
        x-(cat_scale/5),y+(cat_scale)*1/3);
    }
  }

至此,动画完成。

手绘与码绘的对比

在动画这个应用上,其实两者各有千秋。手绘能做到画面更加精致有更多细节,更能体现质感,但同时,它又太过费时。而码绘在运动这一方面有着得天独厚的优势,它能更平滑的完成动画操作。

发现的问题

码绘在建立场景的过程中,发现对于环境色这一概念,几乎还是一个空白领域。

猜你喜欢

转载自blog.csdn.net/qq_38406696/article/details/84405445
今日推荐