I always loved the look and feel of Google’s Material Design components, especially the ripple effect on the buttons. And I realized it had been some time since I did something with processing so I decided to replicate it in processing and here is the result:
They are not particularly perfect but they work and look pretty good! I made the code as simple as I could (but it’s still kinda sphagetti). I am also going to make a material design slider, text field and some other ui elements, I hope I will be able to release all of them in a library. Feedback would be appreciated!
Note: One thing I’m not really happy about is that you need to use text offset along with the text size correctly to properly align the text on the button. I will try doing that a bit simpler.
Button:
/*
Material Design Button | by qewer3322 | 2020
PUBLIC PROPERTIES
- x (int | x pos of the button)
- y (int | y pos of the button)
- w (int | width of the button)
- h (int | height of the button)
- mainColor (color | color of the button)
- textColor (color | color of the button text)
- effectColor (color | color of hover and ripple effects)
- icon (PImage | image icon of the button)
- text (String | text of the button)
- textFont (PFont | font of the button text)
- textSize (int | size of the button text)
- textOffsetX (int | offset of the text on the x axis)
- textOffsetY (int | offset of the text on the y axis)
- hoverFadeSpeed (int | controls hover fade speed, default value is 5)
- ripple speed (int | controls the speed of the ripple effect, default value is 12)
- rippleFadeSpeed (int | controls the fade speed of the ripple effect, default value is 5)
PUBLIC FUNCTIONS
- mouseOver() (boolean | returns true when mouse hovers the button, can be used with an if statement in mouseClicked() to do something when the button is clicked)
- display() (displays the button, should be called in draw() in order for the button to work)
- mousePress() (registers mouse press events, should be called in mousePressed() in order for the button to work )
- setIcon(PImage icon) (sets the image icon of the button, optional)
- setColor(color c) (sets the color of the button, optional)
- setColor(int r, int g, int b) (sets the color of the button, optional)
- setEffectColor(color c) (sets the color of hover and ripple effects, optional)
- setEffectColor(int r, int g, int b) (sets the color of hover and ripple effects, optional)
- setText(String text) (sets the button text)
- setTextColor(color c) (sets the color of the button text)
- setTextColor(int r, int g, int b) (sets the color of the button text)
- setTextFont(PFont font) (sets the font of the button text)
- setTextSize(int size) (sets the size of the button text)
- setTextOffset(int offsetX, int offsetY) (sets the x and y offset of the button text)
Please keep this top header if you share this code
*/
class QButton {
//x pos, y pos, width, height and corner radius
int x;
int y;
int w;
int h;
int r;
//color of the button
color mainColor = color(159, 72, 217);
//button icon
PImage icon;
//button text
String text = "";
//PGraphics object (used for the ripple effect)
private PGraphics pg;
//PGraphics mask object (used for the ripple effect)
private PGraphics maskPg;
//constructor 1
QButton(int x1, int y1, int w1, int h1, int r1) {
x = x1;
y = y1;
w = w1;
h = h1;
r = r1;
pg = createGraphics(w, h);
maskPg = createGraphics(w, h);
}
//constructor2
QButton(int x1, int y1, int w1, int h1, int r1, String text1) {
x = x1;
y = y1;
w = w1;
h = h1;
r = r1;
text = text1;
pg = createGraphics(w, h);
maskPg = createGraphics(w, h);
}
//text font
PFont textFont;
//font boolean
private boolean fontB = false;
//text color
color textColor = color(255);
//text size
int textSize = 25;
//text offset on x axis
int textOffsetX = 20;
//text offset on y axis
int textOffsetY = 20;
//icon boolean
private boolean iconB = false;
//color of the ripple and hover
color effectColor = color(255);
//hover fade speed
int hoverFadeSpeed = 5;
//hover opacity
private int hOpacity = 0;
//ripple diameter
int rd = 0;
//ripple x and y
int rx = 0;
int ry = 0;
//ripplevspeed
int rippleSpeed = 15;
//ripple boolean
private boolean ripple = false;
//ripple opacity
private int rOpacity = 0;
//ripple fade speed
int rippleFadeSpeed = 5;
//public fuctions
//display function (should be called in draw() for the button to show)
void display() {
noStroke();
//pgraphics (ripple)
image(pg, x, y);
//hover rect
fill(effectColor, hOpacity);
rect(x, y, w, h, r);
//text
if (fontB) {
textFont(font);
}
textSize(textSize);
fill(textColor);
if (! iconB) {
text(text, x + textOffsetX, y + textOffsetY);
}
if (iconB) {
image(icon, x + int(h/4), y + int(h/4), h - int(h/2), h - int(h/2));
text(text, x + h + textOffsetX, y + textOffsetY);
}
hover();
betterRipple();
//ripple();
}
//mouse press function (should be called in mousePressed() for the button to register mouse press events)
void mousePress() {
if (overRect(x, y, w, h)) {
rd = 0;
rx = mouseX - x;
ry = mouseY - y;
ripple = true;
rOpacity = 80;
}
}
//getters
boolean mouseOver() {
if (overRect(x, y, w, h)) {
return true;
} else return false;
}
//setters
void setColor(color c) {
mainColor = c;
}
void setColor(int r, int g, int b) {
mainColor = color(r, g, b);
}
void setEffectColor(color c) {
effectColor = c;
}
void setEffectColor(int r, int g, int b) {
effectColor = color(r, g, b);
}
void setText(String str) {
text = str;
}
void setTextColor(color c) {
textColor = c;
}
void setTextColor(int r, int g, int b) {
textColor = color(r, g, b);
}
void setTextFont(PFont font) {
fontB = true;
textFont = font;
}
void setTextSize(int s1) {
textSize = s1;
}
void setTextOffset(int x1, int y1) {
textOffsetX = x1;
textOffsetY = y1;
}
void setIcon(PImage img) {
icon = img;
iconB = true;
}
//private fuctions
//hover effect function
private void hover() {
if (overRect(x, y, w, h) && hOpacity < 50) {
hOpacity += hoverFadeSpeed;
} else if (hOpacity > 0) {
hOpacity -= hoverFadeSpeed;
}
}
//ripple test
private void betterRipple() {
if (ripple) {
rd += rippleSpeed;
}
if (rx > w/2 && ry > h/2 && rd > dist(0,0,rx,ry) * 2) {
ripple = false;
}
if (rx >= w/2 && ry <= h/2 && rd > dist(0,h,rx,ry) * 2) {
ripple = false;
}
if (rx < w/2 && ry > h/2 && rd > dist(w,0,rx,ry) * 2) {
ripple = false;
}
if (rx <= w/2 && ry <= h/2 && rd > dist(w,h,rx,ry) * 2) {
ripple = false;
}
if (ripple == false) {
rOpacity -= rippleFadeSpeed;
}
pg.beginDraw();
pg.noStroke();
pg.fill(mainColor);
pg.rect(0, 0, w, h, r);
pg.fill(effectColor, rOpacity);
pg.ellipse(rx, ry, rd, rd);
pg.endDraw();
maskPg.beginDraw();
maskPg.rect(0, 0, w, h, r);
maskPg.endDraw();
pg.mask(maskPg);
}
private boolean overRect(int x, int y, int width, int height) {
if (mouseX >= x && mouseX <= x+width &&
mouseY >= y && mouseY <= y+height) {
return true;
} else {
return false;
}
}
}
Circle Button:
/*
/*
Material Design Circle Button | by qewer3322 | 2020
PUBLIC PROPERTIES
- mouseOver() (boolean | becomes true when mouse hovers the button, can be used with an if statement in mouseClicked() to do something when the button is clicked)
- x (int | x pos of the button)
- y (int | y pos of the button)
- d (int | diameter of the button)
- mainColor (color | color of the button)
- effectColor (color | color of hover and ripple effects)
- icon (PImage | image icon of the button)
- hoverFadeSpeed (int | controls hover fade speed, default value is 5)
- ripple speed (int | controls the speed of the ripple effect, default value is 8)
- rippleFadeSpeed (int | controls the fade speed of the ripple effect, default value is 5)
PUBLIC FUNCTIONS
- display() (displays the button, should be called in draw() in order for the button to work)
- mousePress() (registers mouse press events, should be called in mousePressed() in order for the button to work )
- setIcon(PImage icon) (sets the image icon of the button, optional)
- setColor(color c) (sets the color of the button, optional)
- setColor(int r, int g, int b) (sets the color of the button, optional)
- setEffectColor(color c) (sets the color of hover and ripple effects, optional)
- setEffectColor(int r, int g, int b) (sets the color of hover and ripple effects, optional)
Please keep this top header if you share this code
*/
class QCircleButton {
// x pos, y pos and diameter
int x;
int y;
int d;
//color of the button
color mainColor = color(159, 72, 217);
//button icon
PImage icon;
//constructor
QCircleButton(int x1, int y1, int d1) {
x = x1;
y = y1;
d = d1;
}
//icon boolean
private boolean iconB = false;
//color of the ripple and hover
color effectColor = color(255);
//hover fade speed
int hoverFadeSpeed = 5;
//hover opacity
private int hOpacity = 0;
//ripple speed
int rippleSpeed = 8;
//ripple boolean
private boolean ripple = false;
//ripple fade speed
int rippleFadeSpeed = 5;
//ripple circle diameter
private int rd = 0;
//ripple opacity (used for fade)
private int rOpacity = 80;
//display function (should be called in draw() for the button to show)
void display() {
//button
noStroke();
fill(mainColor);
ellipse(x, y, d, d);
//hover circle
noStroke();
fill(effectColor, hOpacity);
ellipse(x, y, d, d);
//ripple circle
noStroke();
fill(effectColor, rOpacity);
ellipse(x, y, rd, rd);
if (rd > d) {
rd = d;
}
//icon
if (iconB) {
image(icon, x - (d/1.5)/2, y - (d/1.5)/2, d/1.5, d/1.5);
}
hover();
ripple();
}
//mouse press function (should be called in mousePressed() for the button to register mouse press events)
void mousePress() {
if (overCircle(x, y, d)) {
ripple = true;
rd = 0;
rOpacity = 80;
}
}
void setColor(color c) {
mainColor = c;
}
void setColor(int r, int g, int b) {
mainColor = color(r, g, b);
}
void setEffectColor(color c) {
effectColor = c;
}
void setEffectColor(int r, int g, int b) {
effectColor = color(r, g, b);
}
void setIcon(PImage img) {
icon = img;
iconB = true;
}
boolean mouseOver() {
if (overCircle(x, y, d)) {
return true;
} else return false;
}
//hover effect function
private void hover() {
if (overCircle(x, y, d) && hOpacity < 50) {
hOpacity += hoverFadeSpeed;
} else if (hOpacity > 0) {
hOpacity -= hoverFadeSpeed;
}
}
//ripple effect function
private void ripple() {
if (ripple == true && rd < d) {
rd += rippleSpeed;
}
if (rd == d) {
ripple = false;
}
if (ripple == false) {
rOpacity -= rippleFadeSpeed;
}
}
private boolean overCircle(int x, int y, int diameter) {
float disX = x - mouseX;
float disY = y - mouseY;
if (sqrt(sq(disX) + sq(disY)) < diameter/2 ) {
return true;
} else {
return false;
}
}
}
Demo:
PImage plus;
QCircleButton b1 = new QCircleButton(320, 320, 80);
PFont font;
PImage star;
QButton b2;
void setup() {
size(640, 640);
plus = loadImage("plus.png");
star = loadImage("star.png");
font = createFont("Roboto-Medium.ttf", 48);
new QButton(210, 170, 220, 60, 15, "BUTTON");
}
void draw() {
background(get(0, 0));
b1.setIcon(plus);
b1.display();
b2.setTextSize(35);
b2.setTextFont(font);
b2.setTextOffset(2, 44);
b2.setIcon(star);
b2.display();
}
void mousePressed() {
b1.mousePress();
b2.mousePress();
}
Assets for the demo:
Edit: Updated the button ripple effect.