正如 Niccolo 建议的那样,首先分解您的问题,取出您不需要的任何东西,然后一步一步走。
根据您对目标的描述,在术语或取出内容方面,听起来代码与播放电影没有任何关系,因此取出该代码以简化。 (我能想到使用电影的唯一原因是使用它的当前时间来控制线条,但除非你想要电影背景,否则你不应该需要它)
在 draw() 循环中有一些看起来很奇怪的东西:
-
if(time>= table.getFloat(row_no, 4)){ 假设 .csv 文件中的第 5 列保存当前行的提示时间(以秒为单位),此条件可能仅触发一次,因为没有任何增量 row_no
-
line(table.getFloat(row_no,0),table.getFloat(row_no, 1), a, b); 可能不是您想要的,并且可能是一个错字,因为 a 和 b 已从相同的 .csv 列(0 和 1)中检索到,这意味着您是行的开始和结束位置在确切的位置(因此不渲染一条线)。也许你的意思是line(table.getFloat(row_no,2),table.getFloat(row_no, 3), a, b);?
-
a = a + 2; 和 b = b + 2;:其中一个点的 x,y 位置可能会偏移 2,但在此点之后永远不会使用新位置。在下一次 draw() 迭代中,a 和 b 被重新定义,因此偏移量丢失。
让我们来分解问题:
我在一个文件中有坐标和秒数,我想让一条线逐渐变大。在文件中,我有 x1、x2(关于第一个点坐标)、x2、y2(第二个点坐标)以及我想在第二个点方向上开始增长线的时间(以秒为单位)。
- 加载并解析数据(包含行坐标 (x1,y1,x2,y2) 和时间(以秒为单位)的行)
- “使线条逐渐变大” - 根据指定时间(以秒为单位)在当前行(.csv 行坐标)和下一行之间设置动画(插值)
更进一步,如果从头开始,更多的是“如何做”:
- 在一个值之间插入另一个值(暂时忽略时间,保持任务独立且尽可能简单)
- 跟踪时间作为值之间插值的参数
- 解析 .csv 行
- 在 4 个四个值之间进行插值
- 根据动画时间递增 .csv 行
幸运的是,Processing 提供了一个内置函数,用于在值之间进行线性插值lerp()(lerp = 简称线性插值)。
它需要三个参数:
- 从(开始)动画的值
- 动画到(停止)的值
- 一个插值量(介于 0.0 和 1.0 之间)
它返回一个值:开始值和停止值之间的值。
将插值量视为百分比(0 = 0%、0.5 = 50%、1.0 = 100%)。
这里有一个基本的草图来说明这个概念:
void draw(){
background(255);
//map time to an interpolation normalized value
float t = map(mouseX,0,width,0.0,1.0);
//interpolate the values
float size = lerp(10,90,constrain(t,0.0,1.0));
ellipse(50,50,size,size);
}
你可以在下面运行 sn-p a p5.js 演示:
function setup() {
createCanvas(100,100);
}
function draw(){
background(255);
//map time to an interpolation normalized value
var t = map(mouseX,0,width,0.0,1.0);
//interpolate the values
var size = lerp(10,90,constrain(t,0.0,1.0));
ellipse(50,50,size,size);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.11/p5.min.js"></script>
如果您在 x 轴上移动鼠标,您会注意到大小在 10 到 90 像素之间,具体取决于鼠标位置。
第 1 步完成!
接下来,您可以使用millis() 来跟踪时间,只需在时间之间使用map()(以毫秒为单位)(将开始时间和结束时间之间的当前时间映射到 0.0 - 1.0 范围):
float seconds = 3.5;
//the time in millis since the last update
int previousMillis;
//the current time in millis
int currentMillis;
//the time in millis until the next event
int nextMillis;
void setup(){
previousMillis = millis();
currentMillis = millis();
nextMillis = currentMillis + (int)(seconds * 1000);
}
void draw(){
background(255);
//update the current timer continously
currentMillis = millis();
//map the current time from the previous to the next time as a normalized value (0,0 to 1.0) range
float t = map(currentMillis,previousMillis,nextMillis,0.0,1.0);
//interpolate the values
float size = lerp(10,90,constrain(t,0.0,1.0));
ellipse(50,50,size,size);
}
作为 p5.js 可运行演示:
var seconds = 3.5;
//the time in millis since the last update
var previousMillis;
//the current time in millis
var currentMillis;
//the time in millis until the next event
var nextMillis;
function setup(){
previousMillis = millis();
currentMillis = millis();
nextMillis = currentMillis + (int)(seconds * 1000);
}
function draw(){
background(255);
//update the current timer continously
currentMillis = millis();
//map the current time from the previous to the next time as a normalized value (0,0 to 1.0) range
var t = map(currentMillis,previousMillis,nextMillis,0.0,1.0);
//interpolate the values
var size = lerp(10,90,constrain(t,0.0,1.0));
ellipse(50,50,size,size);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.11/p5.min.js"></script>
第 2 步已完成,看起来您已经掌握了第 3 步(解析 .csv 值):)
一旦您了解了如何插入一个值,第 4 步应该是微不足道的:只需对不同的变量执行 4 次!
第 5 步很简单,您只需检查 row_no 是否超出表的行数。
row_no = row_no + 1;
if(row_no > table.getRowCount()) row_no = 0;
您可以做的一件巧妙的事情是使用modulo operator(%) 来做与数学表达式相同的事情:
row_no = (row_no + 1) % table.getRowCount();
总之,假设你的 .csv 文件看起来有点像这样:
x1,y1,x2,y2,seconds
5,5,95,5,4
95,5,95,95,3
95,95,5,95,2
5,95,5,5,1
将它们放在一起的一种方法是这样的:
Table table;
int row_no = 0;
//the time in millis since the last update
int previousMillis;
//the current time in millis
int currentMillis;
//the time in millis until the next event
int nextMillis;
//where to animate the line from
float x1Start,y1Start,x2Start,y2Start;
//where to animate the line to
float x1Stop,y1Stop,x2Stop,y2Stop;
//currently interpolated line positions
float x1Lerp,y1Lerp,x2Lerp,y2Lerp;
void setup(){
size(100,100);
strokeWeight(3);
fill(0);
table = loadTable("data/new.csv","header, csv");
//printing the table in console, handy just for debugging/double checking values
println("data");
for(int i = 0 ; i <= table.getRowCount(); i++){
print(table.getColumnTitle(i)+"\t");
}
println();
for(int i = 0 ; i < table.getRowCount(); i++){
TableRow row = table.getRow(i);
for(int j = 0; j < row.getColumnCount(); j++){
print(row.getFloat(j) + "\t");
}
println();
}
//fetch lines and seconds from the current and next rows
updateFromRow();
}
void updateFromRow(){
//update times
previousMillis = millis();
currentMillis = millis();
//get the current row and it's line coordinates
TableRow currentRow = table.getRow(row_no);
x1Start = currentRow.getFloat("x1");
y1Start = currentRow.getFloat("y1");
x2Start = currentRow.getFloat("x2");
y2Start = currentRow.getFloat("y2");
//get the next row and it's line coordinates - notice % module is used to easily loop back to 0 once row_no goes beyond the number of table rows
TableRow nextRow = table.getRow((row_no + 1) % table.getRowCount());
x1Stop = nextRow.getFloat("x1");
y1Stop = nextRow.getFloat("y1");
x2Stop = nextRow.getFloat("x2");
y2Stop = nextRow.getFloat("y2");
//get the duration in seconds, convert it to millis( * 1000) and add it to the current millis
nextMillis = currentMillis + (int)(currentRow.getFloat("seconds") * 1000);
println("updated from row: " + row_no);
}
void draw(){
background(255);
//update the current timer continously
currentMillis = millis();
//map the current time from the previous to the next time as a normalized value (0,0 to 1.0) range
float t = map(currentMillis,previousMillis,nextMillis,0.0,1.0);
//if the current interpolation value is above 1.0
if(t > 1.0){
t = 0;
//increment the row
row_no++;
//if the current row counter is above the total number of rows, reset back 0
if(row_no >= table.getRowCount()){
row_no = 0;
}
//update values from incremented row (lines and their coordinates + time)
updateFromRow();
}
text("t:"+t,15,20);
//constrain interpolated value between 0.0 and 1.0
float interpolationAmount = constrain(t,0.0,1.0);
//linearly interpolate (lerp) between the current and next line coordinates
x1Lerp = lerp(x1Start,x1Stop,interpolationAmount);
y1Lerp = lerp(y1Start,y1Stop,interpolationAmount);
x2Lerp = lerp(x2Start,x2Stop,interpolationAmount);
y2Lerp = lerp(y2Start,y2Stop,interpolationAmount);
//finally, render the interpolated line
line(x1Lerp,y1Lerp,x2Lerp,y2Lerp);
}
在跟踪数据方面,可以使用 PVector 实例存储线坐标(...来自 .csv 数据的 x,y 分量 set() 和线点可以通过 @ 成对插值987654327@)。
到目前为止,我们只使用线性插值,但您可能还想查看easing 和tweening。