Special Effect: A Fading Circle¶
In these notes you will learn:
- How to make an object fade away by changing its transparency.
- How to make an object “pulse” by repeatedly fading in and out.
- How to make an object bounce around the screen while pulsing.
Introduction¶
So far, all the animations we’ve created move an object by changing its position. In this note we will see another kind of animation: fading.
Organizing Our Demo Program¶
The first step in writing any program is to decide exactly what it should do. Since we are writing a demonstration of a new kind of animation, we want to keep it relatively simple. So lets agree that the program works as follows: when the user clicks on the screen, a circle appears at the click-point and then slowly fades away to invisibility.
We’ll need variables to store its (x, y) center. The circle won’t (initially)
be moving or changing shape, so we don’t need dx
, dy
, or diam
.
How do we get the circle to fade? Fading means that the circle starts out totally opaque (i.e. not see-through), and then slowly becomes more transparent.
Recall that Processing lets you specify the alpha value of a color, which sets its transparency. Alpha values in Processing range from 0 to 255, where 0 is totally transparent (i.e. invisible), and 255 is totally opaque (i.e. not transparent at all).
So we will implement fading by slowly decreasing the alpha value of the circle’s fill color.
First Draft: Just the Circle¶
Lets write a program that displays a big blue circle wherever the user clicks with the mouse. There is no fading yet; we’ll add that next after we get this working:
float x;
float y;
void setup() {
size(500, 500);
smooth();
}
void draw() {
background(255);
noStroke();
fill(0, 0, 255);
ellipse(x, y, 150, 150);
}
void mousePressed() {
x = mouseX;
y = mouseY;
}
Recall that the mousePressed()
function is automatically called by
Processing whenever the user clicks a mouse button. When it’s called
here, it sets x
and y
to be the position of the mouse, and so
when draw()
is called the circle will be drawn centered at the
most recent mouse-click.
Adding Fading¶
Now lets add the fading effect. We’ll need a variable to keep track of the current transparency:
float transparency; // ranges from 0 to 1 (0 = totally transparent)
Lets agree that transparency
ranges from 0 (totally transparent)
to 1 (totally opaque). A range of 0 to 1 is pretty common because it
makes it easy to talk about percentages, e.g. 0.73 is exactly 73% of
the way between 0 and 1.
We will also need a variable that tells us how much to change the
transparency on each call to draw()
. In other words, we need a
variable that stores how fast the transparency change:
float dtrans;
We call this variable dtrans
following the convention of starting
“speed” variables (like dx
and dy
) with the letter d
.
Now we can add the fading effect. We need to make only a few changes:
Whenever the user clicks the mouse, the circle is drawn at a new location with a totally opaque transparency. Thus we need to set
transparency
to 1 at the end ofmouseClick()
:void mousePressed() { x = mouseX; y = mouseY; transparency = 1; }
Whenever the circle is drawn, its fill color must be drawn taking the transparency into account. This is not completely straightforward. For instance, this doesn’t work:
void draw() { background(255); noStroke(); fill(0, 0, 255, transparency); // wrong! ellipse(x, y, 150, 150); }
The problem is that the
fill
function expects a value in the range 0 to 255 (you learn this by reading the fill() function documentation). Buttransparency
ranges from 0 to 1.We could, of course, change our previous decision and let
transparency
range from 0 to 255. That would be fine for this one program. But in general, that’s probably a bad idea. In large programs, you may have many different ranges, and if these ranges all have different begin and end points you will soon find it takes a lot off mental effort to keep track of them. It’s simpler and more consistent if you require that all ranges start at 0 and end at 1.So lets keep the range of
transparency
as it is, and instead use themap
function to converttransparency
into a corresponding value in the range 0 to 255:void draw() { background(255); noStroke(); float a = map(transparency, 0, 1, 0, 255); fill(0, 0, 255, a); ellipse(x, y, 150, 150); }
This works! The expression
map(transparency, 0, 1, 0, 255)
returns the a number in the range 0 to 255 that corresponds to the same value oftransparency
from 0 to 1.After the circle is drawn, we need to change
transparency
by addingdtrans
to it:void draw() { background(255); noStroke(); float a = map(transparency, 0, 1, 0, 255); fill(0, 0, 255, a); ellipse(x, y, 150, 150); transparency += dtrans; }
Since we’ve decided to add
dtrans
totransparency
(instead of subtracting it), we must remember to initializedtrans
to a negative value in order to get a fading effect.
This works! Wherever you click on the window a circle appears and then slowly fades to away.
Warning
There’s a bug in this program that doesn’t matter much in this simple demo,
but could cause problems in larger programs that use the same idea. The
problem is that transparency
quickly becomes negative, which means it
is outside of the valid range 0 to 1. That causes no problem here, but in
other situations it might. So to ensure that transparency
is always in
the valid range, we can modify the code by adding a new statement:
// ...
transparency += dtrans;
transparency = constrain(transparency, 0, 1);
The statement transparency = constrain(transparency, 0, 1)
limits the
value of transparency
to be between 0 and 1. It is the same as writing this
if-statement:
// ...
transparency += dtrans;
if (transparency < 0) {
transparency = 0;
} else if (transparency > 1) {
transparency = 1
}
Fading in and Fading Out¶
Now lets change the program so that after the circle becomes invisible, it starts to fade back in at the same rate that it faded out. The trick here is to use if-statements to check when the circle is totally invisible/opaque and then reverse the direction of the fading:
void draw() {
// ...
transparency += dtrans;
if (transparency < 0) {
dtrans = -dtrans;
} else if (transparency > 1) {
dtrans = -dtrans;
}
}
We could also have written the if-statement like this:
void draw() {
// ...
transparency += dtrans;
if (transparency < 0) {
dtrans = -dtrans;
}
if (transparency > 1) {
dtrans = -dtrans;
}
}
Or even this:
void draw() {
// ...
transparency += dtrans;
if (transparency < 0 || transparency > 1) {
dtrans = -dtrans;
}
}
For this particular program all three ways of writing the if-statement are practically equivalent, and so it does not matter too much which one you use. But, in general, writing if-statements in different forms can lead to significantly different code.
The statement dtran = -dtrans
causes the “direction” of the fading to
change by flipping the sign of dtrans
, i.e. when dtrans
is negative it
becomes positive (fading in), and when it is positive it becomes negative
(fading out).
Bouncing and Fading¶
Finally, lets make the circle bounce around the screen while fading. We’ve
seen in previous notes how to make a ball bounce, and so we need to add the
same sort of code here. In particular, to make the circle move we’ll use
dx
and dy
to represent the velocity, and then add some if-statements
that test for edge collisions. See the code at the end of these notes for the
final program.
Questions¶
- What are alpha values?
- What is the range of alpha values in a Processing color function
such as
fill
? - When is
mousePressed()
called? - Why does the variable
transparency
range from 0 to 1? - Why is
dtrans
set to a negative value in the fading circle program?
Programming Questions¶
- Add a background image to the fading circle program. As the circle fades away, the underlying image should become clearer.
- Modify the fading circle program so the circle fades in (instead of fades out). When the user clicks with the mouse, the circle begins invisible and then slowly becomes fully opaque.
- Read about the tint() function and then modify the fading circle program to make an arbitrary image (loaded from a file) fade away.
The Fading Circle Program¶
float x;
float y;
float transparency; // 0 <= transparency <= 1
float dtrans; // the change in transparency on each call to draw
void setup() {
size(500, 500);
smooth();
transparency = 1;
dtrans = -0.01;
}
void draw() {
background(255);
noStroke();
float a = map(transparency, 0, 1, 0, 255);
fill(0, 0, 255, a);
ellipse(x, y, 150, 150);
transparency += dtrans;
}
void mousePressed() {
x = mouseX;
y = mouseY;
transparency = 1;
}
The Pulsing Circle Program¶
float x;
float y;
float transparency; // 0 <= transparency <= 1
float dtrans; // the change in transparency on each call to draw
void setup() {
size(500, 500);
smooth();
transparency = 1;
dtrans = -0.01;
}
void draw() {
background(255);
noStroke();
float a = map(transparency, 0, 1, 0, 255);
fill(0, 0, 255, a);
ellipse(x, y, 150, 150);
transparency += dtrans;
if (transparency < 0) {
dtrans = -dtrans;
} else if (transparency > 1) {
dtrans = -dtrans;
}
}
void mousePressed() {
x = mouseX;
y = mouseY;
transparency = 1;
}
The Bouncing and Pulsing Circle Program¶
float x;
float y;
float dx;
float dy;
float transparency; // 0 <= transparency <= 1
float dtrans; // the change in transparency on each call to draw
void setup() {
size(500, 500);
smooth();
x = 250;
y = 250;
dx = -1.5;
dy = 2.2;
transparency = 1;
dtrans = -0.01;
}
void draw() {
background(255);
noStroke();
float a = map(transparency, 0, 1, 0, 255);
fill(0, 0, 255, a);
ellipse(x, y, 150, 150);
x += dx;
y += dy;
if (x - 75 < 0) { // hit left edge?
x = 75;
dx = -dx;
}
if (x + 75 > 499) { // hit right edge?
x = 499 - 75;
dx = -dx;
}
if (y - 75 < 0) { // hit top edge?
y = 75;
dy = -dy;
}
if (y + 75 > 499) { // hit bottom edge?
y = 499 - 75;
dy = -dy;
}
transparency += dtrans;
if (transparency < 0) {
dtrans = -dtrans;
} else if (transparency > 1) {
dtrans = -dtrans;
}
}
void mousePressed() {
x = mouseX;
y = mouseY;
transparency = 1;
}