Xin Wei
8 years ago
9 changed files with 566 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||
.firebaserc |
|||
node_modules |
@ -0,0 +1,31 @@ |
|||
# Image Maker |
|||
|
|||
This sample shows how to create various images through Functions and serve it to the client |
|||
|
|||
It uses [node-canvas](https://github.com/Automattic/node-canvas) to create a canvas environment on Node. That canvas is then used to create either a clock, sparkline chart, or sphere(s) in png format. The images are then cached on the server and sent to the client in `image/png` format. |
|||
|
|||
## Setting up the sample |
|||
|
|||
1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com). |
|||
1. Clone or download this repo and open the `image-maker` directory. |
|||
1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`. |
|||
1. Configure the CLI locally by using `firebase use --add` and select your project in the list. |
|||
1. Install dependencies locally by running: `cd functions; npm install;` |
|||
|
|||
|
|||
## Deploy and test |
|||
|
|||
This sample comes with a web-based UI for testing the function. To test it out: |
|||
|
|||
1. Deploy your project using `firebase deploy` |
|||
1. Open the app using `firebase open hosting:site`, this will open a browser.` |
|||
|
|||
|
|||
## Contributing |
|||
|
|||
We'd love that you contribute to the project. Before doing so please read our [Contributor guide](../CONTRIBUTING.md). |
|||
|
|||
|
|||
## License |
|||
|
|||
© Google, 2017. Licensed under an [Apache-2](../LICENSE) license. |
@ -0,0 +1,8 @@ |
|||
{ |
|||
"hosting": { |
|||
"public": "public", |
|||
"rewrites": [ |
|||
{"source":"/api/**", "function":"app" } |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,117 @@ |
|||
const _ = require('lodash'); |
|||
|
|||
const getDefaultOpts = () => ({ |
|||
strokes: { |
|||
clock: '#325FA2', |
|||
hour: '#000000', |
|||
minute: '#000000', |
|||
seconds: '#D40000' |
|||
}, |
|||
fills: { |
|||
clock: '#eeeeee', |
|||
tip: '#555555', |
|||
seconds: '#D40000' |
|||
} |
|||
}); |
|||
|
|||
const getX = (angle) => { |
|||
return -Math.sin(angle + Math.PI) |
|||
}; |
|||
|
|||
const getY = (angle) => { |
|||
return Math.cos(angle + Math.PI) |
|||
}; |
|||
|
|||
const clock = (ctx, colorOpts) => { |
|||
const colors = _.merge({}, getDefaultOpts(), colorOpts); |
|||
let x, y, i; |
|||
const now = new Date(); |
|||
|
|||
ctx.clearRect(0, 0, 320, 320); |
|||
ctx.save(); |
|||
|
|||
// Clock background
|
|||
ctx.translate(160, 160); |
|||
ctx.beginPath(); |
|||
ctx.lineWidth = 14; |
|||
ctx.strokeStyle = colors.strokes.clock; |
|||
ctx.fillStyle = colors.fills.clock; |
|||
ctx.arc(0, 0, 142, 0, Math.PI * 2, true); |
|||
ctx.stroke(); |
|||
ctx.fill(); |
|||
|
|||
// Hour marks
|
|||
ctx.lineWidth = 8; |
|||
ctx.strokeStyle = colors.strokes.hour; |
|||
for (i = 0; i < 12; i++) { |
|||
x = getX(Math.PI / 6 * i); |
|||
y = getY(Math.PI / 6 * i); |
|||
ctx.beginPath(); |
|||
ctx.moveTo(x * 100, y * 100); |
|||
ctx.lineTo(x * 125, y * 125); |
|||
ctx.stroke(); |
|||
} |
|||
|
|||
// Minute marks
|
|||
ctx.lineWidth = 5; |
|||
ctx.strokeStyle = colors.strokes.minute; |
|||
for (i = 0; i < 60; i++) { |
|||
if (i % 5 !== 0) { |
|||
x = getX(Math.PI / 30 * i); |
|||
y = getY(Math.PI / 30 * i); |
|||
ctx.beginPath(); |
|||
ctx.moveTo(x * 117, y * 117); |
|||
ctx.lineTo(x * 125, y * 125); |
|||
ctx.stroke(); |
|||
} |
|||
} |
|||
|
|||
const sec = now.getSeconds(); |
|||
const min = now.getMinutes(); |
|||
const hr = now.getHours() % 12; |
|||
|
|||
ctx.fillStyle = 'black'; |
|||
|
|||
// Write hours
|
|||
x = getX(hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec); |
|||
y = getY(hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec); |
|||
ctx.lineWidth = 14; |
|||
ctx.beginPath(); |
|||
ctx.moveTo(x * -20, y * -20); |
|||
ctx.lineTo(x * 80, y * 80); |
|||
ctx.stroke(); |
|||
|
|||
// Write minutes
|
|||
x = getX((Math.PI / 30) * min + (Math.PI / 1800) * sec); |
|||
y = getY((Math.PI / 30) * min + (Math.PI / 1800) * sec); |
|||
|
|||
ctx.lineWidth = 10; |
|||
ctx.beginPath(); |
|||
ctx.moveTo(x * -28, y * -28); |
|||
ctx.lineTo(x * 112, y * 112); |
|||
ctx.stroke(); |
|||
|
|||
// Write seconds
|
|||
x = getX(sec * Math.PI / 30); |
|||
y = getY(sec * Math.PI / 30); |
|||
ctx.strokeStyle = colors.strokes.seconds; |
|||
ctx.fillStyle = colors.fills.seconds; |
|||
ctx.lineWidth = 6; |
|||
ctx.beginPath(); |
|||
ctx.moveTo(x * -30, y * -30); |
|||
ctx.lineTo(x * 83, y * 83); |
|||
ctx.stroke(); |
|||
ctx.beginPath(); |
|||
ctx.arc(0, 0, 10, 0, Math.PI * 2, true); |
|||
ctx.fill(); |
|||
ctx.beginPath(); |
|||
ctx.arc(x * 95, y * 95, 10, 0, Math.PI * 2, true); |
|||
ctx.stroke(); |
|||
ctx.fillStyle = colors.fills.tip; |
|||
ctx.arc(0, 0, 3, 0, Math.PI * 2, true); |
|||
ctx.fill(); |
|||
|
|||
ctx.restore(); |
|||
} |
|||
|
|||
module.exports = clock; |
@ -0,0 +1,70 @@ |
|||
/** |
|||
* Copyright 2016 Google Inc. All Rights Reserved. |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
const functions = require('firebase-functions'); |
|||
const app = require('express')(); |
|||
const Canvas = require('canvas-prebuilt'); |
|||
const _ = require('lodash'); |
|||
|
|||
const clock = require('./clock'); |
|||
const spark = require('./sparkline'); |
|||
const ray = require('./ray'); |
|||
|
|||
app.get('/api/ray', (req, res) => { |
|||
const tracers = JSON.parse(req.query.tracers); |
|||
if (!_.isArray(tracers) || |
|||
!_.every(tracers, (depth) => typeof depth === 'number')) { |
|||
//invalid format
|
|||
res.status(422); |
|||
res.end(); |
|||
} |
|||
const canvas = new Canvas(243 * tracers.length, 243); |
|||
const ctx = canvas.getContext('2d'); |
|||
for (var i=0; i<tracers.length; i++) { |
|||
ray(Math.round(27/tracers[i]), 81, ctx, { x: 243, y: 0 }); |
|||
} |
|||
res.set('Cache-Control', 'public, max-age=60, s-maxage=31536000'); |
|||
res.writeHead(200, { 'Content-Type': 'image/png' }) |
|||
canvas.pngStream().pipe(res); |
|||
}); |
|||
|
|||
app.get('/api/clock', (req, res) => { |
|||
const colorOpts = req.query; |
|||
const canvas = new Canvas(320, 320) |
|||
const ctx = canvas.getContext('2d') |
|||
clock(ctx, colorOpts); |
|||
res.set('Cache-Control', 'public, max-age=60, s-maxage=31536000'); |
|||
res.writeHead(200, { 'Content-Type': 'image/png' }) |
|||
canvas.pngStream().pipe(res); |
|||
}); |
|||
|
|||
app.get('/api/spark', (req, res) => { |
|||
const dataSeries = JSON.parse(req.query.series); |
|||
const colorOpts = req.query.colorOpts || {}; |
|||
if (!_.isArray(dataSeries) || !_.every(dataSeries, (num) => typeof num === 'number')) { |
|||
//invalid format
|
|||
res.status(422); |
|||
res.end(); |
|||
} |
|||
const canvas = new Canvas(320, 100); |
|||
var ctx = canvas.getContext('2d'); |
|||
spark(ctx, dataSeries, colorOpts); |
|||
res.set('Cache-Control', 'public, max-age=60, s-maxage=31536000'); |
|||
res.writeHead(200, { 'Content-Type': 'image/png' }) |
|||
canvas.pngStream().pipe(res); |
|||
}); |
|||
|
|||
exports.app = functions.https.onRequest(app); |
@ -0,0 +1,11 @@ |
|||
{ |
|||
"name": "functions-image-maker", |
|||
"description": "Sample functions that generate images on the backend", |
|||
"dependencies": { |
|||
"canvas-prebuilt": "1.6.5-prerelease.1", |
|||
"express": "4.15.2", |
|||
"firebase-admin": "4.2.1", |
|||
"firebase-functions": "0.5.6", |
|||
"lodash": "4.17.4" |
|||
} |
|||
} |
@ -0,0 +1,67 @@ |
|||
const render = (min, max, ctx, posObject) => { |
|||
ctx.fillStyle = getPointColor(122, 122); |
|||
ctx.fillRect(0, 0, 240, 240); |
|||
renderLevel(min, max, 0, ctx); |
|||
ctx.translate(posObject.x, posObject.y); |
|||
}; |
|||
|
|||
const renderLevel = (minimumLevel, level, y, ctx) => { |
|||
let x; |
|||
|
|||
for (x = 0; x < 243 / level; ++x) { |
|||
drawBlock(x, y, level, ctx); |
|||
} |
|||
for (x = 0; x < 243 / level; x += 3) { |
|||
drawBlock(x, y + 1, level, ctx); |
|||
drawBlock(x + 2, y + 1, level, ctx); |
|||
} |
|||
for (x = 0; x < 243 / level; ++x) { |
|||
drawBlock(x, y + 2, level, ctx); |
|||
} |
|||
if ((y += 3) >= 243 / level) { |
|||
y = 0; |
|||
level /= 3; |
|||
} |
|||
if (level >= minimumLevel) { |
|||
renderLevel(minimumLevel, level, y, ctx); |
|||
} |
|||
}; |
|||
|
|||
const drawBlock = (x, y, level, ctx) => { |
|||
ctx.fillStyle = getPointColor( |
|||
x * level + (level - 1) / 2, |
|||
y * level + (level - 1) / 2 |
|||
); |
|||
|
|||
ctx.fillRect( |
|||
x * level, |
|||
y * level, |
|||
level, |
|||
level |
|||
); |
|||
}; |
|||
|
|||
const getPointColor = (x, y) => { |
|||
x = x / 121.5 - 1; |
|||
y = -y / 121.5 + 1; |
|||
|
|||
const x2y2 = x * x + y * y; |
|||
if (x2y2 > 1) { |
|||
return '#000'; |
|||
} |
|||
|
|||
const root = Math.sqrt(1 - x2y2); |
|||
const x3d = x * 0.7071067812 + root / 2 - y / 2; |
|||
const y3d = x * 0.7071067812 - root / 2 + y / 2; |
|||
const z3d = 0.7071067812 * root + 0.7071067812 * y; |
|||
let brightness = -x / 2 + root * 0.7071067812 + y / 2; |
|||
if (brightness < 0) brightness = 0; |
|||
|
|||
const r = Math.round(brightness * 127.5 * (1 - y3d)); |
|||
const g = Math.round(brightness * 127.5 * (x3d + 1)); |
|||
const b = Math.round(brightness * 127.5 * (z3d + 1)); |
|||
|
|||
return 'rgb(' + r + ', ' + g + ', ' + b + ')'; |
|||
} |
|||
|
|||
module.exports = render; |
@ -0,0 +1,23 @@ |
|||
const spark = (ctx, data, opts) => { |
|||
const len = data.length; |
|||
const pad = 1; |
|||
const width = ctx.canvas.width; |
|||
const height = ctx.canvas.height; |
|||
const barWidth = width / len; |
|||
const max = Math.max.apply(null, data); |
|||
ctx.fillStyle = opts.barFill || 'rgba(0,0,255,0.5)'; |
|||
ctx.strokeStyle = opts.lineStroke || 'red'; |
|||
ctx.lineWidth = 1; |
|||
|
|||
data.forEach((n, i) => { |
|||
const x = i * barWidth + pad; |
|||
const y = height * (n / max); |
|||
|
|||
ctx.lineTo(x, height - y); |
|||
ctx.fillRect(x, height, barWidth - pad, -y); |
|||
}) |
|||
|
|||
ctx.stroke(); |
|||
}; |
|||
|
|||
module.exports = spark; |
@ -0,0 +1,237 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="description" content="Demonstrates how to dynamically generate images on the server"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Image Maker Samples</title> |
|||
|
|||
<!-- Material Design Lite --> |
|||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> |
|||
<link rel="stylesheet" href="https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css"> |
|||
<script defer src="https://code.getmdl.io/1.1.3/material.min.js"></script> |
|||
<style> |
|||
.mdl-card { |
|||
width: 400px; |
|||
margin: 10px; |
|||
} |
|||
section.row { |
|||
display: flex; |
|||
flex-direction: row; |
|||
width: 100%; |
|||
justify-content: center; |
|||
margin: 10px 0; |
|||
align-items: baseline; |
|||
} |
|||
.inline { |
|||
display: inline-block; |
|||
margin: 0 5px; |
|||
} |
|||
.image-container { |
|||
text-align: center; |
|||
display: none; |
|||
} |
|||
#ray-tracing { |
|||
min-width: 400px; |
|||
width: auto; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header"> |
|||
<!-- Header section containing title --> |
|||
<header class="mdl-layout__header mdl-color-text--white mdl-color--light-blue-700"> |
|||
<div class="mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid"> |
|||
<div class="mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop"> |
|||
<h3>Image Maker Samples</h3> |
|||
</div> |
|||
</div> |
|||
</header> |
|||
<main class="mdl-layout__content mdl-color--grey-100"> |
|||
<div class="mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid" style="align-items: baseline;"> |
|||
<section class='row'> |
|||
<div class="mdl-card mdl-shadow--2dp"> |
|||
<div class="mdl-card__title"> |
|||
<h2 class="mdl-card__title-text">Clock</h2> |
|||
</div> |
|||
<div class="mdl-card__supporting-text" id='clock-directions'>Generates a PNG of a clock based on the following parameters.</div> |
|||
<div class="mdl-card__supporting-text image-container" id='clock-png'></div> |
|||
<div class="mdl-card__supporting-text"> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='clock-stroke' value='#325FA2' /> |
|||
<label class="mdl-textfield__label" for="clock-stroke">Clock Stroke</label> |
|||
</div> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='clock-fill' value='#eeeeee' /> |
|||
<label class="mdl-textfield__label" for="clock-fill">Clock Fill</label> |
|||
</div> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='hour-stroke' value='#000000' /> |
|||
<label class="mdl-textfield__label" for="hour-stroke">Hour Hand Stroke</label> |
|||
</div> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='minute-stroke' value='#000000' /> |
|||
<label class="mdl-textfield__label" for="minute-stroke">Minute Hand Stroke</label> |
|||
</div> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='seconds-stroke' value='#D40000' /> |
|||
<label class="mdl-textfield__label" for="seconds-stroke">Seconds Hand Stroke</label> |
|||
</div> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='seconds-fill' value='#D40000' /> |
|||
<label class="mdl-textfield__label" for="seconds-fill">Seconds Hand Fill</label> |
|||
</div> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='seconds-tip' value='#555555' /> |
|||
<label class="mdl-textfield__label" for="seconds-tip">Seconds Tip Fill</label> |
|||
</div> |
|||
</div> |
|||
<div class="mdl-card__actions mdl-card--border"> |
|||
<a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" onClick={generateClock()}> |
|||
Generate Image |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="mdl-card mdl-shadow--2dp"> |
|||
<div class="mdl-card__title"> |
|||
<h2 class="mdl-card__title-text">Sparkline Chart</h2> |
|||
</div> |
|||
<div class="mdl-card__supporting-text" id='spark-directions'>Generates a PNG of a Sparkline Chart. Input an array of integers as a sample data series.</div> |
|||
<div class="mdl-card__supporting-text image-container" id='spark-png'></div> |
|||
<div class="mdl-card__supporting-text"> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%"> |
|||
<textarea class="mdl-textfield__input" type="text" rows= "1" id="sampleSeries">[1, 2, 4, 5, 10, 4, 2, 5, 4, 3, 3, 2]</textarea> |
|||
<label class="mdl-textfield__label" for='sampleSeries'>Sample Data Series</label> |
|||
</div> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='line-stroke' value='#D40000' /> |
|||
<label class="mdl-textfield__label" for="line-stroke">Line Stroke</label> |
|||
</div> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline" style="width:46%"> |
|||
<input class="mdl-textfield__input" type='color' id='bar-fill' value='#EEEEEE' /> |
|||
<label class="mdl-textfield__label" for="bar-fill">Bar Fill</label> |
|||
</div> |
|||
</div> |
|||
<div class="mdl-card__actions mdl-card--border"> |
|||
<a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" onClick={generateSparkChart()}> |
|||
Generate Image |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
<section class='row'> |
|||
<div class="mdl-card mdl-shadow--2dp" id='ray-tracing'> |
|||
<div class="mdl-card__title"> |
|||
<h2 class="mdl-card__title-text">Ray Tracing</h2> |
|||
</div> |
|||
<div class="mdl-card__supporting-text" id='ray-directions'>Generates a PNG of a sphere using a recursive ray tracing algorithm. </div> |
|||
<div class='image-container' id='ray-png'></div> |
|||
<div id="ray-sections"> |
|||
<div class="mdl-card__supporting-text mdl-card--border raySection"> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%"> |
|||
<input class="mdl-textfield__input" type="number" value="27" min="1" max="27" id="depth" /> |
|||
<label class="mdl-textfield__label" for="depth">Min Depth Level</label> |
|||
</div> |
|||
</div> |
|||
<div class="mdl-card__supporting-text mdl-card--border raySection"> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%"> |
|||
<input class="mdl-textfield__input" type="number" value="1" min="1" max="27" id="depth" /> |
|||
<label class="mdl-textfield__label" for="depth">Min Depth Level</label> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="mdl-card__actions mdl-card--border"> |
|||
<a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" onClick={generateTracers()}> |
|||
Generate Image |
|||
</a> |
|||
<a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" onClick={addTracer()}> |
|||
Add New Tracer |
|||
</a> |
|||
<a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" onClick={removeTracer()}> |
|||
Remove Last Tracer |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
</main> |
|||
</div> |
|||
<script |
|||
src="https://code.jquery.com/jquery-2.2.4.min.js" |
|||
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" |
|||
crossorigin="anonymous"> |
|||
</script> |
|||
<script> |
|||
function updateImage(divId, imageUrl) { |
|||
$(`#${divId}-png`).html(`<div class='loading'>generating...</div><img src='${imageUrl}'/>`); |
|||
$(`#${divId}-png img`).on('load', function() { |
|||
$(`#${divId}-png .loading`).remove(); |
|||
}); |
|||
$(`#${divId}-png`).css('display','block'); |
|||
$(`#${divId}-directions`).css('display','hidden'); |
|||
} |
|||
function addTracer() { |
|||
$('#ray-sections').append(`<div class="mdl-card__supporting-text mdl-card--border raySection"> |
|||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width:100%"> |
|||
<input class="mdl-textfield__input" type="number" value="13" min="1" max="27" id="depth" /> |
|||
<label class="mdl-textfield__label" for="depth">Min Depth Level</label> |
|||
</div> |
|||
</div>`); |
|||
componentHandler.upgradeElements(document.getElementById('ray-sections')); |
|||
} |
|||
function removeTracer() { |
|||
if ($('#ray-sections').children().length > 1) { |
|||
$('#ray-sections').children().last().remove(); |
|||
} |
|||
} |
|||
function generateTracers() { |
|||
const tracers = $('.raySection').map(function(ind, elm) { |
|||
return parseInt($(elm).find("input#depth").val()) |
|||
}); |
|||
// construct data obj |
|||
const data = { |
|||
tracers: JSON.stringify(tracers.toArray()) |
|||
}; |
|||
// construct URL |
|||
const url = `/api/ray?${$.param(data)}`; |
|||
// update the img-container |
|||
updateImage('ray', url); |
|||
} |
|||
function generateSparkChart() { |
|||
// construct data obj |
|||
const data = { |
|||
series: $('#sampleSeries').val(), |
|||
colorOpts: { |
|||
lineStroke: $('#line-stroke').val(), |
|||
barFill: $('#bar-fill').val() |
|||
} |
|||
}; |
|||
// construct URL |
|||
const url = `/api/spark?${$.param(data)}`; |
|||
// update the img-container |
|||
updateImage('spark', url); |
|||
} |
|||
function generateClock() { |
|||
// construct data obj |
|||
const data = { |
|||
strokes: { |
|||
clock: $('#clock-stroke').val(), |
|||
minute: $('#hour-stroke').val(), |
|||
hour: $('#minute-stroke').val(), |
|||
seconds: $('#seconds-stroke').val() |
|||
}, |
|||
fills: { |
|||
clock: $('#clock-fill').val(), |
|||
tip: $('#seconds-tip').val(), |
|||
seconds: $('#seconds-fill').val() |
|||
} |
|||
}; |
|||
// construct URL |
|||
const url = `/api/clock?${$.param(data)}`; |
|||
// update the img-container |
|||
updateImage('clock', url); |
|||
} |
|||
</script> |
|||
</body> |
|||
</html> |
Loading…
Reference in new issue