p5.js实现故宫橘猫赏秋图动画


Posted in Javascript onOctober 23, 2019

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

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

这里先放效果图:

板绘插图

p5.js实现故宫橘猫赏秋图动画

码绘效果图

p5.js实现故宫橘猫赏秋图动画

这里强烈建议直接运行代码!!!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个点,然后画三角形,形成一个没有四肢,没有尾巴的橘猫。

p5.js实现故宫橘猫赏秋图动画

尾巴用了贝塞尔曲线,坐标也跟中心点关联。

猫的四肢是运动视觉的关键!!!动画之所以能动是因为有承上启下的连续性动作。猫行走从侧面看过去就是两腿相互交叉变换。所以在写动画逻辑之前你需要先画出关键帧状态。
关键帧状态确定了就可开始着手动画逻辑:首先视觉上我们先要营造出猫在原地踏步的感觉。我们有两个关键帧状态,所以可以运用模运算,在运动的中心坐标基础上模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);
 }
 }

至此,动画完成。

手绘与码绘的对比

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

发现的问题

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery 查找select ,并触发事件的实现代码
Mar 30 Javascript
jquery maxlength使用说明
Sep 09 Javascript
javascript中的self和this用法小结
Feb 08 Javascript
JS实现距离上次刷新已过多少秒示例
May 23 Javascript
jQuery中removeData()方法用法实例
Dec 27 Javascript
jQuery同步提交示例代码
Dec 12 Javascript
AngularJS基于ngInfiniteScroll实现下拉滚动加载的方法
Dec 14 Javascript
jQuery分页插件jquery.pagination.js使用方法解析
Feb 09 Javascript
jQuery插件FusionCharts绘制的2D条状图效果【附demo源码】
May 13 jQuery
vue.js 左侧二级菜单显示与隐藏切换的实例代码
May 23 Javascript
详解vue2父组件传递props异步数据到子组件的问题
Jun 29 Javascript
js实现小星星游戏
Mar 23 Javascript
vue父组件给子组件的组件传值provide inject的方法
Oct 23 #Javascript
p5.js临摹旋转爱心
Oct 23 #Javascript
JavaScript 作用域scope简单汇总
Oct 23 #Javascript
node.js使用fs读取文件出错的解决方案
Oct 23 #Javascript
jquery 键盘事件 keypress() keydown() keyup()用法总结
Oct 23 #jQuery
JavaScript提升机制Hoisting详解
Oct 23 #Javascript
使用p5.js实现动态GIF图片临摹重现
Oct 23 #Javascript
You might like
PHP实现多进程并行操作的详解(可做守护进程)
2013/06/18 PHP
php中count获取多维数组长度的方法
2014/11/03 PHP
php通过asort()给关联数组按照值排序的方法
2015/03/18 PHP
Yii2中简单的场景使用介绍
2017/06/02 PHP
详细解读php的命名空间(二)
2018/02/21 PHP
两个DIV等高的JS的实现代码
2007/12/23 Javascript
jquery 问答知识整理
2010/02/11 Javascript
一个javascript图片阅览组件
2010/11/09 Javascript
jquery 按键盘上的enter事件
2014/05/11 Javascript
Java File类的常用方法总结
2015/03/18 Javascript
js实现的黑背景灰色二级导航菜单效果代码
2015/08/24 Javascript
15个常用的jquery代码片段
2015/12/19 Javascript
ReactNative Image组件使用详解
2017/08/07 Javascript
解决vue组件中使用v-for出现告警问题及v for指令介绍
2017/11/11 Javascript
vue-resource请求实现http登录拦截或者路由拦截的方法
2018/07/11 Javascript
js页面加载后执行的几种方式小结
2020/01/30 Javascript
[01:25]2014DOTA2国际邀请赛 zhou分析LGD比赛情况
2014/07/14 DOTA
[58:12]Ti4第二日主赛事败者组 LGD vs iG 3
2014/07/21 DOTA
[01:10:49]Secret vs VGJ.S 2018国际邀请赛淘汰赛BO3 第二场 8.24
2018/08/25 DOTA
Python下的常用下载安装工具pip的安装方法
2015/11/13 Python
详解Django+Uwsgi+Nginx 实现生产环境部署
2018/11/06 Python
python中pip的使用和修改下载源的方法
2019/07/08 Python
Python配置pip国内镜像源的实现
2020/08/20 Python
python 用Matplotlib作图中有多个Y轴
2020/11/28 Python
python判断all函数输出结果是否为true的方法
2020/12/03 Python
10种CSS3实现的loading动画,挑一个走吧?
2020/11/16 HTML / CSS
通过HTML5规范搞定i、em、b、strong元素的区别
2017/03/04 HTML / CSS
科室工作的个人自我评价
2013/10/30 职场文书
生产副总岗位职责
2013/11/28 职场文书
大学生职业规划范文:象牙塔生活的四年计划
2014/01/14 职场文书
销售人员自我评价
2014/02/01 职场文书
计算机网络专业自荐信
2014/07/04 职场文书
写得不错的求职信范文
2014/07/11 职场文书
领导干部学习心得体会
2016/01/23 职场文书
JAVA长虹键法之建造者Builder模式实现
2022/04/10 Java/Android
vue生命周期钩子函数以及触发时机
2022/04/26 Vue.js