This is an example of:
- Day vs night mode
- Order of drawing (background first)
- LerpColor for smooth transitions
license: mit |
<head> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.js"></script> | |
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script> | |
<script language="javascript" type="text/javascript" src="clock.js"></script> | |
<script language="javascript" type="text/javascript" src="runner.js"></script> | |
<style> | |
body { padding: 0; margin: 0; } | |
.inner { position: absolute; } | |
#controls { | |
font: 300 12px "Helvetica Neue"; | |
padding: 5; | |
margin: 5; | |
background: #f0f0f0; | |
opacity: 0.0; | |
-webkit-transition: opacity 0.2s ease; | |
-moz-transition: opacity 0.2s ease; | |
-o-transition: opacity 0.2s ease; | |
-ms-transition: opacity 0.2s ease; | |
} | |
#controls:hover { opacity: 0.9; } | |
</style> | |
</head> | |
<body style="background-color:white"> | |
<div class="outer"> | |
<div class="inner"> | |
<div id="canvasContainer"></div> | |
<a href="part1.html">part1</a> | |
<a href="part2.html">part2</a> | |
<a href="clock.html">clock</a> | |
<a href="debug.html">debug</a> | |
</div> | |
<div class="inner" id="controls" height="500px"> | |
<table> | |
<tr> | |
<td>Timer</td> | |
<td id="slider1Container"></td> | |
</tr> | |
<tr> | |
<td>Alarm Set</td> | |
<td id="checkContainer1"></td> | |
</tr> | |
<tr> | |
<td>Alarm Active</td> | |
<td id="checkContainer2"></td> | |
</tr> | |
</table> | |
</div> | |
</div> | |
<br> | |
</body> |
/* | |
* use p5.js to draw a clock on a 960x500 canvas | |
*/ | |
function draw_clock(obj) { | |
const night_sky = color(50, 50, 60); | |
const night_ground = color(30, 60, 30); | |
const day_sky = color(100, 100, 230); | |
const day_ground = color(100, 200, 100); | |
const sun_yellow = color(240, 240, 0); | |
// draw your own clock here based on the values of obj: | |
// obj.hours goes from 0-23 | |
// obj.minutes goes from 0-59 | |
// obj.seconds goes from 0-59 | |
// obj.millis goes from 0-999 | |
// obj.seconds_until_alarm is: | |
// < 0 if no alarm is set | |
// = 0 if the alarm is currently going off | |
// > 0 --> the number of seconds until alarm should go off | |
let current_ground = null; | |
if (obj.hours >= 7 && obj.hours < 8) { | |
// sunrise | |
let hour_fraction = obj.minutes / 60; | |
let cur_sky = lerpColor(night_sky, day_sky, hour_fraction); | |
current_ground = lerpColor(night_ground, day_ground, hour_fraction); | |
background(cur_sky); | |
} | |
else if (obj.hours >= 19 && obj.hours < 20) { | |
// sunrise | |
let hour_fraction = obj.minutes / 60; | |
let cur_sky = lerpColor(day_sky, night_sky, hour_fraction); | |
current_ground = lerpColor(day_ground, night_ground, hour_fraction); | |
background(cur_sky); | |
} | |
else if(obj.hours >= 8 && obj.hours < 20) { | |
// daytime | |
background(day_sky); | |
current_ground = day_ground; | |
} | |
else { | |
// nightime | |
background(night_sky); | |
current_ground = night_ground; | |
} | |
let sun_height = null; | |
if (obj.hours <= 12) { | |
sun_height = map(obj.hours, 0, 12, height, 0); | |
} | |
else { | |
sun_height = map(obj.hours, 12, 23, 0, height); | |
} | |
fill(sun_yellow); | |
ellipse(width/2, sun_height, 100); | |
fill(current_ground); | |
rect(0, height/2, width, height); | |
// debug helper | |
textSize(50) | |
text("debug:", 50, 100) | |
textSize(100) | |
text(obj.hours, 200, 100) | |
} |
/* | |
* use p5.js to draw a clock on a 960x500 canvas | |
*/ | |
function draw_clock(obj) { | |
// draw your own clock here based on the values of obj: | |
// obj.hours goes from 0-23 | |
// obj.minutes goes from 0-59 | |
// obj.seconds goes from 0-59 | |
// obj.millis goes from 0-999 | |
// obj.seconds_until_alarm is: | |
// < 0 if no alarm is set | |
// = 0 if the alarm is currently going off | |
// > 0 --> the number of seconds until alarm should go off | |
background(0); | |
const ballWidth = 100; | |
fill(255, 255, 0); | |
let secs = obj.seconds; | |
let millis = obj.millis; | |
let exactSeconds = secs + millis / 1000.0; | |
posX = map(exactSeconds, 0, 60, ballWidth/2, width-ballWidth/2); | |
posY = map(60, 0, 100, 0, height); | |
ellipse(posX, posY, ballWidth); | |
fill(255); | |
posX = map(obj.minutes, 0, 59, ballWidth/2, width-ballWidth/2); | |
posY = map(40, 0, 100, 0, height); | |
ellipse(posX, posY, ballWidth); | |
posX = map(obj.hours, 0, 23, ballWidth/2, width-ballWidth/2); | |
posY = map(20, 0, 100, 0, height); | |
ellipse(posX, posY, ballWidth); | |
} |
/* | |
* us p5.js to draw a clock on a 960x500 canvas | |
*/ | |
function draw_clock(obj) { | |
// draw your own clock here based on the values of obj: | |
// obj.hours goes from 0-23 | |
// obj.minutes goes from 0-59 | |
// obj.seconds goes from 0-59 | |
// obj.millis goes from 0-999 | |
// obj.seconds_until_alarm is: | |
// < 0 if no alarm is set | |
// = 0 if the alarm is currently going off | |
// > 0 --> the number of seconds until alarm should go off | |
let hours = obj.hours; | |
let minutes = obj.minutes; | |
let seconds = obj.seconds; | |
let millis = obj.millis; | |
let alarm = obj.seconds_until_alarm; | |
background(255,255,200); // beige | |
fill(128,100,100); // dark grey | |
text("Hour: " + hours, 10, 22); | |
text("Minute: " + minutes, 10, 42); | |
text("Second: " + seconds, 10, 62); | |
text("Millis: " + millis, 10, 82); | |
let hourBarWidth = map(hours, 0, 23, 0, width); | |
let minuteBarWidth = map(minutes, 0, 59, 0, width); | |
let secondBarWidth = map(seconds, 0, 59, 0, width); | |
let millisBarWidth = map(millis, 0, 1000, 0, width); | |
noStroke(); | |
fill(40); | |
rect(0, 100, hourBarWidth, 50); | |
fill(80); | |
rect(0, 150, minuteBarWidth, 50); | |
fill(120) | |
rect(0, 200, secondBarWidth, 50); | |
fill(160) | |
rect(0, 250, millisBarWidth, 50); | |
// Make a bar which *smoothly* interpolates across 1 minute. | |
// We calculate a version that goes from 0...60, | |
// but with a fractional remainder: | |
let secondBarWidthChunky = map(seconds, 0, 59, 0, width); | |
let secondsWithFraction = seconds + (millis / 1000.0); | |
let secondBarWidthSmooth = map(secondsWithFraction, 0, 60, 0, width); | |
fill(100, 100, 200) | |
rect(0, 350, secondBarWidthChunky, 50); | |
fill(120, 120, 240) | |
rect(0, 400, secondBarWidthSmooth, 50); | |
text("Minute: " + secondsWithFraction, 200, 42); | |
fill(200, 100, 100) | |
let alarm_message = "" | |
if(alarm < 0) { | |
alarm_message = "Alarm: Not Set" | |
} | |
else if(alarm == 0) { | |
alarm_message = "Alarm: GOING OFF" | |
} | |
else { | |
let seconds_remaining = int(alarm); | |
alarm_message = "Alarm: will go off in " + seconds_remaining + " seconds" | |
} | |
text(alarm_message, 400, 42); | |
} |
<head> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.js"></script> | |
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script> | |
<script language="javascript" type="text/javascript" src="debug.js"></script> | |
<script language="javascript" type="text/javascript" src="clock.js"></script> | |
<script language="javascript" type="text/javascript" src="runner.js"></script> | |
</head> | |
<body style="background-color:white"> | |
<div class="outer"> | |
<div class="inner"> | |
<div id="canvasContainer"></div> | |
</div> | |
<div class="inner" id="controls" height="500px"> | |
<table> | |
<tr> | |
<td>Timer</td> | |
<td id="slider1Container"></td> | |
</tr> | |
<tr> | |
<td>Alarm Set</td> | |
<td id="checkContainer1"></td> | |
</tr> | |
<tr> | |
<td>Alarm Active</td> | |
<td id="checkContainer2"></td> | |
</tr> | |
</table> | |
</div> | |
</div> | |
</table> | |
<body style="background-color:white"> | |
<div class="outer"> | |
<div class="inner"> | |
<div id="canvasContainer"></div> | |
</div> | |
<div class="inner" id="controls"> | |
<table> | |
<tr> | |
<td>debug</td> | |
<td id="checkboxDebug"></td> | |
</tr> | |
<tr> | |
<td>hours</td> | |
<td id="sliderHours"></td> | |
<td>minutes</td> | |
<td id="sliderMinutes"></td> | |
<td>seconds</td> | |
<td id="sliderSeconds"></td> | |
<td>millis</td> | |
<td id="sliderMillis"></td> | |
</tr> | |
</table> | |
</div> | |
</div> | |
</table> | |
</body> | |
<br> | |
<a href="part1.html">part1</a> | |
<a href="part2.html">part2</a> | |
<a href="clock.html">clock</a> | |
<a href="debug.html">debug</a> | |
</body> |
var DEBUG=true; | |
var debugCheckbox; | |
var hourSlider; | |
var minSlider; | |
var secSlider; | |
var millisSlider; | |
var alarmSlider; | |
function debug_setup() { | |
debugCheckbox = createCheckbox('', false); | |
debugCheckbox.parent("checkboxDebug") | |
hourSlider = createSlider(0, 23, 12); | |
hourSlider.parent("sliderHours") | |
minSlider = createSlider(0, 59, 0); | |
minSlider.parent("sliderMinutes") | |
secSlider = createSlider(0, 59, 0); | |
secSlider.parent("sliderSeconds") | |
millisSlider = createSlider(0, 999, 0); | |
millisSlider.parent("sliderMillis") | |
// alarmCheckbox = createCheckbox('', false); | |
// alarmCheckbox.parent("checkboxAlarm") | |
// alarmSlider = createSlider(0, 60, 0); | |
// alarmSlider.parent("sliderAlarm") | |
} |
<head> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.js"></script> | |
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script> | |
<script language="javascript" type="text/javascript" src="clock.js"></script> | |
<script language="javascript" type="text/javascript" src="runner.js"></script> | |
<style> | |
body { padding: 0; margin: 0; } | |
.inner { position: absolute; } | |
#controls { | |
font: 300 12px "Helvetica Neue"; | |
padding: 5; | |
margin: 5; | |
background: #f0f0f0; | |
opacity: 0.0; | |
-webkit-transition: opacity 0.2s ease; | |
-moz-transition: opacity 0.2s ease; | |
-o-transition: opacity 0.2s ease; | |
-ms-transition: opacity 0.2s ease; | |
} | |
#controls:hover { opacity: 0.9; } | |
</style> | |
</head> | |
<body style="background-color:white"> | |
<div class="outer"> | |
<div class="inner"> | |
<div id="canvasContainer"></div> | |
<a href="part1.html">part1</a> | |
<a href="part2.html">part2</a> | |
<a href="clock.html">clock</a> | |
<a href="debug.html">debug</a> | |
</div> | |
<div class="inner" id="controls" height="500px"> | |
<table> | |
<tr> | |
<td>Timer</td> | |
<td id="slider1Container"></td> | |
</tr> | |
<tr> | |
<td>Alarm Set</td> | |
<td id="checkContainer1"></td> | |
</tr> | |
<tr> | |
<td>Alarm Active</td> | |
<td id="checkContainer2"></td> | |
</tr> | |
</table> | |
</div> | |
</div> | |
<br> | |
</body> |
// Update this function to draw you own maeda clock on a 960x500 canvas | |
function draw_clock(obj) { | |
// YOUR MAIN CLOCK CODE GOES HERE | |
background(50); // beige | |
fill(200); // dark grey | |
textSize(40); | |
textAlign(CENTER, CENTER); | |
text("YOUR MAEDA CLOCK CODE GOES HERE", width/2, height/2); | |
} |
<head> | |
<style> body {padding: 0; margin: 0;} </style> | |
</head> | |
<body style="background-color:white"> | |
<img src="sketch.jpg" width="960" height="500"/> | |
<br> | |
<a href="part1.html">part1</a> | |
<a href="part2.html">part2</a> | |
<a href="clock.html">clock</a> | |
<a href="debug.html">debug</a> | |
</body> |
<head> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.js"></script> | |
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script> | |
<script language="javascript" type="text/javascript" src="maeda_clock.js"></script> | |
<script language="javascript" type="text/javascript" src="runner.js"></script> | |
<style> | |
body { padding: 0; margin: 0; } | |
.inner { position: absolute; } | |
#controls { | |
font: 300 12px "Helvetica Neue"; | |
padding: 5; | |
margin: 5; | |
background: #f0f0f0; | |
opacity: 0.0; | |
-webkit-transition: opacity 0.2s ease; | |
-moz-transition: opacity 0.2s ease; | |
-o-transition: opacity 0.2s ease; | |
-ms-transition: opacity 0.2s ease; | |
} | |
#controls:hover { opacity: 0.9; } | |
</style> | |
</head> | |
<body style="background-color:white"> | |
<div class="outer"> | |
<div class="inner"> | |
<div id="canvasContainer"></div> | |
<a href="part1.html">part1</a> | |
<a href="part2.html">part2</a> | |
<a href="clock.html">clock</a> | |
<a href="debug.html">debug</a> | |
</div> | |
<div class="inner" id="controls" height="500px"> | |
<table> | |
<tr> | |
<td>Timer</td> | |
<td id="slider1Container"></td> | |
</tr> | |
<tr> | |
<td>Alarm Set</td> | |
<td id="checkContainer1"></td> | |
</tr> | |
<tr> | |
<td>Alarm Active</td> | |
<td id="checkContainer2"></td> | |
</tr> | |
</table> | |
</div> | |
</div> | |
</table> | |
<br> | |
</body> |
const canvasWidth = 960; | |
const canvasHeight = 500; | |
let prevSec; | |
let millisRolloverTime; | |
let nextAlarm; | |
let debug_is_on = (typeof DEBUG !== 'undefined'); | |
let alarmOverlayCheckbox; | |
let alarmOverlaySlider; | |
let defaultAlarmSliderValue=15; | |
function setup () { | |
// create the drawing canvas, save the canvas element | |
var main_canvas = createCanvas(canvasWidth, canvasHeight); | |
main_canvas.parent('canvasContainer'); | |
alarmOverlaySlider = createSlider(0, 30, defaultAlarmSliderValue); | |
alarmOverlaySlider.parent("slider1Container") | |
alarmOverlaySlider.changed(sliderUpdated); | |
alarmOverlaySlider.mouseMoved(sliderUpdated); | |
alarmOverlaySlider.touchMoved(sliderUpdated); | |
alarmOverlayCheckbox = createCheckbox('', false); | |
alarmOverlayCheckbox.parent('checkContainer1'); | |
alarmOverlayCheckbox.changed(guideChangedEvent); | |
alarmActiveCheckbox = createCheckbox('', false); | |
alarmActiveCheckbox.parent('checkContainer2'); | |
alarmActiveCheckbox.attribute('disabled',''); | |
// this is true if debug.js is included | |
if(debug_is_on) { | |
debug_setup(); | |
} | |
turn_off_alarm(); | |
} | |
function sliderUpdated() { | |
defaultAlarmSliderValue = alarmOverlaySlider.value(); | |
// print("Updated defaultAlarmSliderValue to " + defaultAlarmSliderValue) | |
} | |
function guideChangedEvent() { | |
let alarmIsOn = alarmOverlayCheckbox.checked(); | |
if(alarmIsOn) { | |
turn_on_alarm(); | |
} | |
else { | |
turn_off_alarm(); | |
} | |
redraw(); | |
} | |
function turn_on_alarm() { | |
// disable slider | |
alarmOverlaySlider.attribute('disabled',''); | |
nextAlarm = millis() + defaultAlarmSliderValue * 1000; | |
print("Alarm on: T minus " + defaultAlarmSliderValue + " seconds"); | |
} | |
function turn_off_alarm() { | |
// enable slider back to default | |
alarmOverlaySlider.value(defaultAlarmSliderValue); | |
alarmOverlaySlider.removeAttribute('disabled'); | |
alarmOverlayCheckbox.checked(false); | |
alarmActiveCheckbox.checked(false); | |
print("Alarm now off") | |
nextAlarm = -1; | |
print("Alarm turned off"); | |
} | |
function mouseClicked() { | |
if (debug_is_on && debugCheckbox.checked()) { | |
return; | |
} | |
/* | |
if (nextAlarm > 0) { | |
turn_off_alarm(); | |
} | |
else { | |
turn_on_alarm(); | |
} | |
*/ | |
} | |
// taking ideas from http://cmuems.com/2016/60212/deliverables/deliverables-02/ | |
function draw () { | |
var H, M, S, mils, alarm; | |
if (debug_is_on && debugCheckbox.checked()) { | |
hourSlider.removeAttribute('disabled'); | |
minSlider.removeAttribute('disabled'); | |
secSlider.removeAttribute('disabled'); | |
millisSlider.removeAttribute('disabled'); | |
// alarmCheckbox.removeAttribute('disabled'); | |
// alarmSlider.removeAttribute('disabled'); | |
H = hourSlider.value(); | |
M = minSlider.value(); | |
S = secSlider.value(); | |
mils = millisSlider.value(); | |
// if (alarmCheckbox.checked()) { | |
// alarm = alarmSlider.value(); | |
// } | |
// else { | |
// alarm = -1; | |
// } | |
} | |
else { | |
// Fetch the current time | |
H = hour(); | |
M = minute(); | |
S = second(); | |
if (nextAlarm > 0) { | |
now = millis(); | |
var millis_offset = nextAlarm - now; | |
if (millis_offset < -10000 ){ | |
// turn off alarm | |
nextAlarm = -1; | |
alarm = -1; | |
turn_off_alarm(); | |
} | |
else if (millis_offset < 0) { | |
alarm = 0; | |
alarmOverlaySlider.value(alarm); | |
alarmActiveCheckbox.checked(true); | |
} | |
else { | |
alarm = millis_offset / 1000.0; | |
alarmOverlaySlider.value(alarm); | |
alarmActiveCheckbox.checked(false); | |
} | |
} | |
else { | |
alarm = -1; | |
} | |
// Reckon the current millisecond, | |
// particularly if the second has rolled over. | |
// Note that this is more correct than using millis()%1000; | |
if (prevSec != S) { | |
millisRolloverTime = millis(); | |
} | |
prevSec = S; | |
mils = floor(millis() - millisRolloverTime); | |
if (debug_is_on) { | |
hourSlider.attribute('disabled',''); | |
minSlider.attribute('disabled',''); | |
secSlider.attribute('disabled',''); | |
millisSlider.attribute('disabled',''); | |
// alarmCheckbox.attribute('disabled',''); | |
// alarmSlider.attribute('disabled',''); | |
hourSlider.value(H); | |
minSlider.value(M); | |
secSlider.value(S); | |
millisSlider.value(mils); | |
// alarmCheckbox.checked(alarm >= 0); | |
// alarmSlider.value(alarm); | |
} | |
} | |
obj = {}; | |
obj.hours = H; | |
obj.minutes = M; | |
obj.seconds = S; | |
obj.millis = mils; | |
obj.seconds_until_alarm = alarm; | |
draw_clock(obj); | |
} | |
function keyTyped() { | |
if (key == '!') { | |
saveBlocksImages(); | |
} | |
else if (key == '@') { | |
saveBlocksImages(true); | |
} | |
} |
// note: this file is poorly named - it can generally be ignored. | |
// helper functions below for supporting blocks/purview | |
function saveBlocksImages(doZoom) { | |
if(doZoom == null) { | |
doZoom = false; | |
} | |
// generate 960x500 preview.jpg of entire canvas | |
// TODO: should this be recycled? | |
var offscreenCanvas = document.createElement('canvas'); | |
offscreenCanvas.width = 960; | |
offscreenCanvas.height = 500; | |
var context = offscreenCanvas.getContext('2d'); | |
// background is flat white | |
context.fillStyle="#FFFFFF"; | |
context.fillRect(0, 0, 960, 500); | |
context.drawImage(this.canvas, 0, 0, 960, 500); | |
// save to browser | |
var downloadMime = 'image/octet-stream'; | |
var imageData = offscreenCanvas.toDataURL('image/jpeg'); | |
imageData = imageData.replace('image/jpeg', downloadMime); | |
p5.prototype.downloadFile(imageData, 'preview.jpg', 'jpg'); | |
// generate 230x120 thumbnail.png centered on mouse | |
offscreenCanvas.width = 230; | |
offscreenCanvas.height = 120; | |
// background is flat white | |
context = offscreenCanvas.getContext('2d'); | |
context.fillStyle="#FFFFFF"; | |
context.fillRect(0, 0, 230, 120); | |
if(doZoom) { | |
// pixelDensity does the right thing on retina displays | |
var pd = this._pixelDensity; | |
var sx = pd * mouseX - pd * 230/2; | |
var sy = pd * mouseY - pd * 120/2; | |
var sw = pd * 230; | |
var sh = pd * 120; | |
// bounds checking - just displace if necessary | |
if (sx < 0) { | |
sx = 0; | |
} | |
if (sx > this.canvas.width - sw) { | |
sx = this.canvas.width - sw; | |
} | |
if (sy < 0) { | |
sy = 0; | |
} | |
if (sy > this.canvas.height - sh) { | |
sy = this.canvas.height - sh; | |
} | |
// save to browser | |
context.drawImage(this.canvas, sx, sy, sw, sh, 0, 0, 230, 120); | |
} | |
else { | |
// now scaledown | |
var full_width = this.canvas.width; | |
var full_height = this.canvas.height; | |
context.drawImage(this.canvas, 0, 0, full_width, full_height, 0, 0, 230, 120); | |
} | |
imageData = offscreenCanvas.toDataURL('image/png'); | |
imageData = imageData.replace('image/png', downloadMime); | |
p5.prototype.downloadFile(imageData, 'thumbnail.png', 'png'); | |
} |