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'); | |
| } |