Refactor watch to use modules & webpack! :)
parent
7e0acb45e9
commit
3140f003da
|
@ -1 +1,2 @@
|
|||
node_modules
|
||||
Focus.wgt
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
webpack.ts,tsconfig.json,package.json,package-lock.json,.eslintrc.js,src,node_modules,
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
👋 Hi! This file was autogenerated by tslint-to-eslint-config.
|
||||
https://github.com/typescript-eslint/tslint-to-eslint-config
|
||||
|
||||
It represents the closest reasonable ESLint configuration to this
|
||||
project's original TSLint configuration.
|
||||
|
||||
We recommend eventually switching this configuration to extend from
|
||||
the recommended rulesets in typescript-eslint.
|
||||
https://github.com/typescript-eslint/tslint-to-eslint-config/blob/master/docs/FAQs.md
|
||||
|
||||
Happy linting! 💖
|
||||
*/
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"extends": [
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/dot-notation": "error",
|
||||
"@typescript-eslint/indent": [
|
||||
"error",
|
||||
"tab",
|
||||
{
|
||||
"CallExpression": {
|
||||
"arguments": 1
|
||||
},
|
||||
"FunctionDeclaration": {
|
||||
"parameters": 1
|
||||
},
|
||||
"FunctionExpression": {
|
||||
"parameters": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"error",
|
||||
{
|
||||
"multiline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": true
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "error",
|
||||
"@typescript-eslint/no-empty-function": "error",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-parameter-properties": "off",
|
||||
"@typescript-eslint/no-require-imports": "off",
|
||||
"@typescript-eslint/no-unused-expressions": "error",
|
||||
"@typescript-eslint/no-use-before-define": "error",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/prefer-namespace-keyword": "error",
|
||||
"@typescript-eslint/quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"brace-style": [
|
||||
"error",
|
||||
"stroustrup",
|
||||
{ "allowSingleLine": true }
|
||||
],
|
||||
"comma-dangle": "error",
|
||||
"curly": "off",
|
||||
"default-case": "error",
|
||||
"eol-last": "error",
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
"smart"
|
||||
],
|
||||
"guard-for-in": "error",
|
||||
"id-blacklist": [
|
||||
"error",
|
||||
"any",
|
||||
"Number",
|
||||
"number",
|
||||
"String",
|
||||
"string",
|
||||
"Boolean",
|
||||
"boolean",
|
||||
"Undefined",
|
||||
"undefined"
|
||||
],
|
||||
"id-match": "error",
|
||||
"max-len": [
|
||||
"error", {
|
||||
"code": 150
|
||||
}
|
||||
],
|
||||
"no-bitwise": "error",
|
||||
"no-caller": "error",
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"allow": [
|
||||
"log",
|
||||
"dirxml",
|
||||
"warn",
|
||||
"error",
|
||||
"dir",
|
||||
"timeLog",
|
||||
"assert",
|
||||
"clear",
|
||||
"count",
|
||||
"countReset",
|
||||
"group",
|
||||
"groupCollapsed",
|
||||
"groupEnd",
|
||||
"table",
|
||||
"Console",
|
||||
"markTimeline",
|
||||
"profile",
|
||||
"profileEnd",
|
||||
"timeline",
|
||||
"timelineEnd",
|
||||
"timeStamp",
|
||||
"context"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-debugger": "error",
|
||||
"no-empty": "error",
|
||||
"no-eval": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-trailing-spaces": [
|
||||
"error", {
|
||||
"skipBlankLines": true
|
||||
}
|
||||
],
|
||||
"no-unused-labels": "error",
|
||||
"no-var": "error",
|
||||
"radix": "error",
|
||||
"spaced-comment": [
|
||||
"error",
|
||||
"always",
|
||||
{
|
||||
"markers": [
|
||||
"/"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
BIN
watch/Focus.wgt
BIN
watch/Focus.wgt
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
1.0.5
|
File diff suppressed because one or more lines are too long
|
@ -1,12 +1,9 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<widget xmlns:tizen='http://tizen.org/ns/widgets' xmlns='http://www.w3.org/ns/widgets' id='http://yourdomain/Focus' version='1.0.0' viewmodes='maximized'>
|
||||
<tizen:application id='kFVD8WK8ro.Focus' package='kFVD8WK8ro' required_version='2.3.1' ambient_support='enable'/>
|
||||
<tizen:category name='http://tizen.org/category/wearable_clock'/>
|
||||
<!-- <tizen:category name='http://tizen.org/category/wearable_clock'/> -->
|
||||
<feature name='http://tizen.org/feature/screen.shape.circle'/>
|
||||
<feature name='http://tizen.org/feature/screen.size.all'/>
|
||||
<feature name='http://tizen.org/feature/calendar'/>
|
||||
<feature name='http://tizen.org/feature/calendar.read'/>
|
||||
<feature name='http://tizen.org/feature/calendar.write'/>
|
||||
<content src='index.html'/>
|
||||
<icon src='icon.png'/>
|
||||
<name>Focus</name>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no' />
|
||||
<title>AmbientWatch</title>
|
||||
<title>Focus</title>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
|
@ -13,9 +13,9 @@
|
|||
}
|
||||
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
"description": "Tizen application for the Focus Watch Face.",
|
||||
"main": "build/Main.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon"
|
||||
"dev": "webpack --watch --progress --config webpack.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -31,9 +31,22 @@
|
|||
},
|
||||
"homepage": "https://github.com/Aurailus/Focus#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@types/tizen-common-web": "^2.0.1",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.31.0",
|
||||
"@typescript-eslint/parser": "^4.31.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"eslint": "^7.32.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.3.3",
|
||||
"nodemon": "^2.0.12",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.4.3"
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.4.3",
|
||||
"webpack": "^5.52.1",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-merge": "^5.8.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
import { Event } from './Events';
|
||||
import { degToRad } from './Util';
|
||||
|
||||
/** The width of events. */
|
||||
const EVENT_WIDTH = 22;
|
||||
|
||||
/** The distance inside the event that the title should be drawn. */
|
||||
const TITLE_SPACING = 2;
|
||||
|
||||
/** The text size of the event title. */
|
||||
const TITLE_SIZE = 16;
|
||||
|
||||
// TODO: placeholders
|
||||
const COLOR_LIGHT_DIM = 'rgba(65, 199, 232, 0.25)';
|
||||
|
||||
/**
|
||||
* Handles drawing shapes used by the watch face.
|
||||
* init() loads resources, and **must** be awaited before using any operations.
|
||||
*/
|
||||
|
||||
export default class Artist {
|
||||
readonly radius: number;
|
||||
|
||||
readonly ctx: CanvasRenderingContext2D;
|
||||
private glowCtx: CanvasRenderingContext2D;
|
||||
private glowImg: HTMLImageElement;
|
||||
|
||||
/**
|
||||
* Initializes an Artist to the canvas or canvas context provided.
|
||||
*
|
||||
* @param canvas - The canvas or canvas context to bind the artist to.
|
||||
*/
|
||||
|
||||
constructor(canvas: HTMLCanvasElement | CanvasRenderingContext2D) {
|
||||
if ('getContext' in canvas) this.ctx = canvas.getContext('2d')!;
|
||||
else this.ctx = canvas;
|
||||
this.radius = this.ctx.canvas.width / 2;
|
||||
|
||||
const glowCanvas = document.createElement('canvas');
|
||||
glowCanvas.width = 128;
|
||||
glowCanvas.height = 128;
|
||||
this.glowCtx = glowCanvas.getContext('2d')!;
|
||||
this.glowImg = null as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads resources needed for certain draw operations.
|
||||
*
|
||||
* @returns a promise indicating that the loading is complete.
|
||||
*/
|
||||
|
||||
init(): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
this.glowImg = document.createElement('img');
|
||||
this.glowImg.onload = () => resolve();
|
||||
this.glowImg.src = '../res/glow.png';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the canvas for a new frame.
|
||||
*/
|
||||
|
||||
clear() {
|
||||
const { ctx, radius: canvRadius } = this;
|
||||
ctx.clearRect(0, 0, canvRadius * 2, canvRadius * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a circle on the canvas.
|
||||
*
|
||||
* @param radians - The angle in radians to draw the circle at, starting at the top and moving clockwise.
|
||||
* @param dist - The distance from the center of the canvas to draw the circle at.
|
||||
* @param radius - The radius of the circle to draw.
|
||||
* @param color - The color to draw the circle with.
|
||||
*/
|
||||
|
||||
circle(radians: number, dist: number, radius: number, color: string) {
|
||||
const { ctx, radius: canvRadius } = this;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(canvRadius, canvRadius);
|
||||
ctx.rotate(radians);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(dist, 0, radius, 0, 2 * Math.PI, false);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a line on the canvas.
|
||||
*
|
||||
* @param radians - The angle in radians to draw the line at, starting at the top and moving clockwise.
|
||||
* @param startDist - The distance from the center of the canvas to begin the line at.
|
||||
* @param endDist - The distance from the center of the canvas to end the line at.
|
||||
* @param width - The width of the line to draw.
|
||||
* @param color - The color to draw the line with.
|
||||
*/
|
||||
|
||||
line(radians: number, startDist: number, endDist: number, width: number, color: string) {
|
||||
const { ctx, radius: canvRadius } = this;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(canvRadius, canvRadius);
|
||||
ctx.rotate(radians);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = width;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.moveTo(0, startDist);
|
||||
ctx.lineTo(0, endDist);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a rounded rectangle on the canvas.
|
||||
*
|
||||
* @param radians - The angle in radians to draw the rounded rectangle at, starting at the top and moving clockwise.
|
||||
* @param startDist - The distance from the center of the canvas to begin the rounded rectangle at.
|
||||
* @param endDist - The distance from the center of the canvas to end the rounded rectangle at.
|
||||
* @param width - The width of the rounded rectangle to draw.
|
||||
* @param fill - The color to fill the rounded rectangle with. undefined will result in no fill.
|
||||
* @param stroke - The color to trace the rounded rectangle with. undefined will result in no stroke.
|
||||
* @param strokeWidth - The width of the stroke for the rounded rectangle with. undefined will result in no stroke.
|
||||
*/
|
||||
|
||||
rounded(radians: number, startDist: number, endDist: number, width: number,
|
||||
fill?: string, stroke?: string, strokeWidth?: number ) {
|
||||
const { ctx, radius: canvRadius } = this;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(canvRadius, canvRadius);
|
||||
ctx.rotate(radians);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-width / 2, startDist);
|
||||
ctx.lineTo(-width / 2, endDist);
|
||||
ctx.quadraticCurveTo(-width / 2, endDist + width / 1.5, 0, endDist + width / 1.5);
|
||||
ctx.quadraticCurveTo(width / 2, endDist + width / 1.5, width / 2, endDist);
|
||||
ctx.lineTo(width / 2, startDist);
|
||||
ctx.quadraticCurveTo(width / 2, startDist - width / 1.5, 0, startDist - width / 1.5);
|
||||
ctx.quadraticCurveTo(-width / 2, startDist - width / 1.5, -width / 2, startDist);
|
||||
ctx.closePath();
|
||||
|
||||
if (fill) {
|
||||
ctx.fillStyle = fill;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
if (stroke && strokeWidth) {
|
||||
ctx.strokeStyle = stroke;
|
||||
ctx.lineWidth = strokeWidth;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a glow on the canvas.
|
||||
*
|
||||
* @param radians - The angle in radians to draw the glow at, starting at the top and moving clockwise.
|
||||
* @param dist - The distance from the center of the canvas to draw the glow at.
|
||||
* @param scale - A scale multiplier for the glow's size. A value of 1 results in a size 66% of the canvas size.
|
||||
* @param color - The color to draw the glow with.
|
||||
*/
|
||||
|
||||
glow(radians: number, dist: number, scale: number, color: string) {
|
||||
const { ctx, glowCtx, glowImg, radius: canvRadius } = this;
|
||||
|
||||
const offsetX = Math.cos(radians) * dist;
|
||||
const offsetY = Math.sin(radians) * dist;
|
||||
|
||||
glowCtx.fillStyle = color;
|
||||
glowCtx.globalCompositeOperation = 'source-over';
|
||||
glowCtx.fillRect(0, 0, glowCtx.canvas.width, glowCtx.canvas.height);
|
||||
glowCtx.globalCompositeOperation = 'destination-in';
|
||||
glowCtx.drawImage(glowImg, 0, 0);
|
||||
|
||||
const size = (canvRadius * 1.5) * scale;
|
||||
|
||||
ctx.drawImage(glowCtx.canvas,
|
||||
canvRadius + offsetX - size / 2, canvRadius + offsetY - size / 2,
|
||||
size, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a calendar event on the canvas.
|
||||
*
|
||||
* @param event - The event to draw.
|
||||
* @param dist - The distance from the center of the canvas to draw the event at.
|
||||
*/
|
||||
|
||||
event(event: Event, dist: number) {
|
||||
const { ctx, radius: canvRadius } = this;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(canvRadius, canvRadius);
|
||||
|
||||
const startAngle = degToRad(((event.start.getHours() % 12) +
|
||||
event.start.getMinutes() / 60) / 12 * 360 + EVENT_WIDTH / 6 - 90);
|
||||
const endAngle = degToRad(((event.end.getHours() % 12) +
|
||||
event.end.getMinutes() / 60) / 12 * 360 - EVENT_WIDTH / 8 - 90);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = COLOR_LIGHT_DIM;
|
||||
|
||||
ctx.arc(0, 0, dist, startAngle, endAngle, false);
|
||||
|
||||
let controlPosX = Math.cos(endAngle + degToRad(4.25)) * dist;
|
||||
let controlPosY = Math.sin(endAngle + degToRad(4.25)) * dist;
|
||||
let endPosX = Math.cos(endAngle + degToRad(4.25)) * (dist - EVENT_WIDTH / 2);
|
||||
let endPosY = Math.sin(endAngle + degToRad(4.25)) * (dist - EVENT_WIDTH / 2);
|
||||
|
||||
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||
|
||||
controlPosX = Math.cos(endAngle + degToRad(4.25)) * (dist - EVENT_WIDTH);
|
||||
controlPosY = Math.sin(endAngle + degToRad(4.25)) * (dist - EVENT_WIDTH);
|
||||
endPosX = Math.cos(endAngle) * (dist - EVENT_WIDTH);
|
||||
endPosY = Math.sin(endAngle) * (dist - EVENT_WIDTH);
|
||||
|
||||
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||
|
||||
ctx.arc(0, 0, dist - EVENT_WIDTH, endAngle, startAngle, true);
|
||||
|
||||
controlPosX = Math.cos(startAngle - degToRad(4.25)) * (dist - EVENT_WIDTH);
|
||||
controlPosY = Math.sin(startAngle - degToRad(4.25)) * (dist - EVENT_WIDTH);
|
||||
endPosX = Math.cos(startAngle - degToRad(4.25)) * (dist - EVENT_WIDTH / 2);
|
||||
endPosY = Math.sin(startAngle - degToRad(4.25)) * (dist - EVENT_WIDTH / 2);
|
||||
|
||||
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||
|
||||
controlPosX = Math.cos(startAngle - degToRad(4.25)) * dist;
|
||||
controlPosY = Math.sin(startAngle - degToRad(4.25)) * dist;
|
||||
endPosX = Math.cos(startAngle) * dist;
|
||||
endPosY = Math.sin(startAngle) * dist;
|
||||
|
||||
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
|
||||
ctx.restore();
|
||||
|
||||
this.circle(startAngle, dist - EVENT_WIDTH / 2, EVENT_WIDTH / 2 - 4, event.color);
|
||||
|
||||
ctx.font = `900 ${TITLE_SIZE}px Arial sans-serif`;
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.textAlign = 'center';
|
||||
|
||||
let currentAngle = startAngle + degToRad(96);
|
||||
for (let i = 0; i < event.title.length; i++) {
|
||||
ctx.save();
|
||||
ctx.translate(canvRadius, canvRadius);
|
||||
ctx.rotate(currentAngle);
|
||||
|
||||
ctx.fillText(event.title[i], 0, -dist + TITLE_SPACING);
|
||||
|
||||
ctx.restore();
|
||||
if (i < event.title.length - 1)
|
||||
currentAngle += ctx.measureText(event.title[i]).width * 0.0065 / 2
|
||||
+ ctx.measureText(event.title[i + 1]).width * 0.0065 / 2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
/**
|
||||
* A calendar event.
|
||||
*/
|
||||
|
||||
export interface Event {
|
||||
start: Date;
|
||||
end: Date;
|
||||
title: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current day's calendar events.
|
||||
*/
|
||||
|
||||
export function getEvents(): Event[] {
|
||||
const createDate = (hour: number, minute: number) => {
|
||||
const date = new Date();
|
||||
date.setHours(hour, minute, 0);
|
||||
return date;
|
||||
};
|
||||
|
||||
return [
|
||||
{ start: createDate(9, 30), end: createDate(11, 20), color: '#59acff', title: 'PAAS' },
|
||||
{ start: createDate(12, 30), end: createDate(1, 20), color: '#59acff', title: 'STAT' },
|
||||
{ start: createDate(1, 30), end: createDate(2, 20), color: '#59acff', title: 'MATH' }
|
||||
];
|
||||
}
|
|
@ -1,310 +1,20 @@
|
|||
let theme = {
|
||||
/** 0: none, 1: quarter, 2: hours, 3: hours + minute */
|
||||
notches: 3,
|
||||
/** False: don't display, True: display */
|
||||
events: true,
|
||||
/** False: don't show minute hand, True: show minute hand */
|
||||
minutes: true,
|
||||
/** False: don't show center glow, True: show center glow */
|
||||
glow: true,
|
||||
/** Center glow color. */
|
||||
glow_a: '#4294ff',
|
||||
/** Moving glow 1 color. */
|
||||
glow_b: 'rgba(5, 124, 242, 0.6)',
|
||||
/** Moving glow 2 color. */
|
||||
glow_c: 'rgba(198, 0, 237, 1)',
|
||||
// /** Center glow color. */
|
||||
// glow_a: '#e838ff',
|
||||
// /** Moving glow 1 color. */
|
||||
// glow_b: 'rgba(255, 38, 56, 0.6)',
|
||||
// /** Moving glow 2 color. */
|
||||
// glow_c: 'rgba(179, 38, 255, 1)',
|
||||
}
|
||||
import Artist from './Artist';
|
||||
import Watch from './Watch';
|
||||
|
||||
let image_loaded = false;
|
||||
let window_loaded = false;
|
||||
/**
|
||||
* Main entrypoint to the watchface.
|
||||
*/
|
||||
|
||||
window.onload = () => {
|
||||
window_loaded = true;
|
||||
if (image_loaded) init();
|
||||
};
|
||||
(async () => {
|
||||
await new Promise<void>((resolve) =>
|
||||
window.onload = () => resolve());
|
||||
|
||||
const glow_image = document.createElement('img');
|
||||
glow_image.src = 'res/glow.png';
|
||||
glow_image.onload = () => {
|
||||
image_loaded = true;
|
||||
if (window_loaded) init();
|
||||
};
|
||||
|
||||
const glow_canvas = document.createElement('canvas');
|
||||
glow_canvas.width = 128;
|
||||
glow_canvas.height = 128;
|
||||
const glow_ctx = glow_canvas.getContext('2d')!;
|
||||
|
||||
function init() {
|
||||
const canvas = document.querySelector('canvas')!;
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
canvas.width = document.body.clientWidth;
|
||||
canvas.height = canvas.width;
|
||||
const clockRadius = document.body.clientWidth / 2;
|
||||
|
||||
let rot = 0;
|
||||
let ambient = false;
|
||||
const artist = new Artist(canvas);
|
||||
await artist.init();
|
||||
|
||||
const EVENT_WIDTH = 22;
|
||||
let EVENT_BUFFER = theme.notches === 0 ? 2 : 14;
|
||||
const TITLE_BUFFER = 2;
|
||||
const TITLE_SIZE = 16;
|
||||
const NOTCH_BUFFER = 2;
|
||||
const DATE_SIZE = 20;
|
||||
|
||||
const COLOR_LIGHT_DIM = 'rgba(65, 199, 232, 0.15)';
|
||||
const COLOR_LIGHT_OVERLAY = 'rgba(65, 199, 232, 0.33)';
|
||||
const COLOR_MINUTE_HAND = 'rgba(65, 199, 232, 0.8)';
|
||||
|
||||
const degToRad = (deg: number) => deg * (Math.PI / 180);
|
||||
|
||||
const createDate = (hour: number, minute: number) => {
|
||||
const date = new Date();
|
||||
date.setHours(hour, minute, 0);
|
||||
return date;
|
||||
};
|
||||
|
||||
const events = [
|
||||
{ start: createDate(9, 30), end: createDate(11, 20), color: '#33B679', title: 'PAAS' },
|
||||
{ start: createDate(12, 30), end: createDate(1, 20), color: '#33B679', title: 'STAT' },
|
||||
{ start: createDate(1, 30), end: createDate(2, 20), color: '#33B679', title: 'MATH' }
|
||||
// { start: createDate(9, 30), end: createDate(11, 20), color: '#59acff', title: 'Work' },
|
||||
// { start: createDate(12, 30), end: createDate(1, 20), color: '#59acff', title: 'Walk' },
|
||||
// { start: createDate(1, 30), end: createDate(2, 20), color: '#59acff', title: 'Bike' }
|
||||
];
|
||||
|
||||
function getTime() {
|
||||
try { return tizen.time.getCurrentDateTime(); }
|
||||
catch (e) { return new Date(); }
|
||||
}
|
||||
|
||||
function drawCircle(angle: number, distance: number, width: number, color: string) {
|
||||
ctx.save();
|
||||
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||
ctx.rotate(angle)
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(distance, 0, width / 2, 0, 2 * Math.PI, false);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// function drawLine(startDistance, endDistance, angle, width, color) {
|
||||
// ctx.save();
|
||||
// ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||
// ctx.rotate(degToRad(angle + 90));
|
||||
|
||||
// ctx.beginPath();
|
||||
// ctx.lineWidth = width;
|
||||
// ctx.strokeStyle = color;
|
||||
// ctx.moveTo(startDistance, 0);
|
||||
// ctx.lineTo(endDistance, 0);
|
||||
// ctx.stroke();
|
||||
// ctx.closePath();
|
||||
|
||||
// ctx.restore();
|
||||
// }
|
||||
|
||||
function drawRoundedRect(startDistance: number, endDistance: number, angle: number, width: number,
|
||||
fill?: string, stroke?: string, strokeWidth?: number) {
|
||||
ctx.save();
|
||||
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||
ctx.rotate(angle);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-width / 2, startDistance);
|
||||
ctx.lineTo(-width / 2, endDistance);
|
||||
ctx.quadraticCurveTo(-width / 2, endDistance + width / 1.5, 0, endDistance + width / 1.5);
|
||||
ctx.quadraticCurveTo(width / 2, endDistance + width / 1.5, width / 2, endDistance);
|
||||
ctx.lineTo(width / 2, startDistance);
|
||||
ctx.quadraticCurveTo(width / 2, startDistance - width / 1.5, 0, startDistance - width / 1.5);
|
||||
ctx.quadraticCurveTo(-width / 2, startDistance - width / 1.5, -width / 2, startDistance);
|
||||
ctx.closePath();
|
||||
|
||||
if (fill) {
|
||||
ctx.fillStyle = fill;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
if (stroke && strokeWidth) {
|
||||
ctx.strokeStyle = stroke;
|
||||
ctx.lineWidth = strokeWidth;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
function drawEvent(event: any) {
|
||||
const startAngle = degToRad(((event.start.getHours() % 12) +
|
||||
event.start.getMinutes() / 60) / 12 * 360 + EVENT_WIDTH / 6 - 90);
|
||||
const endAngle = degToRad(((event.end.getHours() % 12) +
|
||||
event.end.getMinutes() / 60) / 12 * 360 - EVENT_WIDTH / 8 - 90);
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = COLOR_LIGHT_DIM;
|
||||
|
||||
ctx.arc(0, 0, clockRadius - EVENT_BUFFER, startAngle, endAngle, false);
|
||||
|
||||
let controlPosX = Math.cos(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER);
|
||||
let controlPosY = Math.sin(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER);
|
||||
let endPosX = Math.cos(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2);
|
||||
let endPosY = Math.sin(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2);
|
||||
|
||||
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||
|
||||
controlPosX = Math.cos(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||
controlPosY = Math.sin(endAngle + degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||
endPosX = Math.cos(endAngle) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||
endPosY = Math.sin(endAngle) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||
|
||||
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||
|
||||
ctx.arc(0, 0, clockRadius - EVENT_BUFFER - EVENT_WIDTH, endAngle, startAngle, true);
|
||||
|
||||
controlPosX = Math.cos(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||
controlPosY = Math.sin(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH);
|
||||
endPosX = Math.cos(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2);
|
||||
endPosY = Math.sin(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2);
|
||||
|
||||
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||
|
||||
controlPosX = Math.cos(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER);
|
||||
controlPosY = Math.sin(startAngle - degToRad(4.25)) * (clockRadius - EVENT_BUFFER);
|
||||
endPosX = Math.cos(startAngle) * (clockRadius - EVENT_BUFFER);
|
||||
endPosY = Math.sin(startAngle) * (clockRadius - EVENT_BUFFER);
|
||||
|
||||
ctx.quadraticCurveTo(controlPosX, controlPosY, endPosX, endPosY);
|
||||
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
|
||||
ctx.restore();
|
||||
|
||||
drawCircle(startAngle, clockRadius - EVENT_BUFFER - EVENT_WIDTH / 2, EVENT_WIDTH - 8, event.color);
|
||||
|
||||
ctx.font = `900 ${TITLE_SIZE}px Arial sans-serif`;
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.textAlign = 'center';
|
||||
|
||||
let currentAngle = startAngle + degToRad(96);
|
||||
for (let i = 0; i < event.title.length; i++) {
|
||||
ctx.save();
|
||||
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||
ctx.rotate(currentAngle);
|
||||
|
||||
ctx.fillText(event.title[i], 0, -(clockRadius - EVENT_BUFFER - TITLE_BUFFER));
|
||||
|
||||
ctx.restore();
|
||||
if (i < event.title.length - 1)
|
||||
currentAngle += ctx.measureText(event.title[i]).width * 0.0065 / 2
|
||||
+ ctx.measureText(event.title[i + 1]).width * 0.0065 / 2;
|
||||
}
|
||||
}
|
||||
|
||||
function drawGlow(offsetX: number, offsetY: number, scaleMult: number, color: string) {
|
||||
glow_ctx.fillStyle = color;
|
||||
glow_ctx.globalCompositeOperation = 'source-over';
|
||||
glow_ctx.fillRect(0, 0, glow_canvas.width, glow_canvas.height);
|
||||
glow_ctx.globalCompositeOperation = 'destination-in';
|
||||
glow_ctx.drawImage(glow_image, 0, 0);
|
||||
|
||||
const scale = 240 * scaleMult;
|
||||
|
||||
ctx.drawImage(glow_canvas,
|
||||
canvas.width / 2 + offsetX - scale / 2,
|
||||
canvas.height / 2 + offsetY - scale / 2,
|
||||
scale, scale);
|
||||
}
|
||||
|
||||
function drawWatchFace() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const time = getTime();
|
||||
|
||||
ctx.font = `900 ${DATE_SIZE}px Arial sans-serif`;
|
||||
ctx.fillStyle = '#aaa';
|
||||
ctx.textBaseline = 'bottom';
|
||||
ctx.textAlign = 'center';
|
||||
|
||||
ctx.fillText(`${['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'][time.getDay()]} ${time.getDate()}`,
|
||||
clockRadius, clockRadius + clockRadius / 1.75);
|
||||
|
||||
ctx.fillStyle = '#666'
|
||||
ctx.fillRect(clockRadius - 1 * DATE_SIZE, clockRadius + clockRadius / 1.75 + 2, 2 * DATE_SIZE, 2);
|
||||
|
||||
if (!ambient) {
|
||||
rot = (rot + 1) % 360;
|
||||
|
||||
let scaleA = 1 + (Math.sin(degToRad(rot + 180)) / 6);
|
||||
let scaleB = 1 + (Math.cos(degToRad(rot)) / 6);
|
||||
let scaleC = 1.3 + (Math.cos(degToRad(rot - 90)) / 6);
|
||||
let offsetX = Math.cos(degToRad(rot + 90)) * 50 + (1 - scaleA);
|
||||
let offsetY = Math.sin(degToRad(rot + 90)) * 50 + (1 - scaleB);
|
||||
|
||||
if (theme.glow) {
|
||||
ctx.globalCompositeOperation = 'lighter';
|
||||
drawGlow(-offsetX, -offsetY, scaleA, theme.glow_b);
|
||||
drawGlow(0, 0, scaleC, theme.glow_a);
|
||||
drawGlow(offsetX, offsetY, scaleB, theme.glow_c);
|
||||
ctx.globalCompositeOperation = 'source-over';
|
||||
}
|
||||
|
||||
if (theme.notches) {
|
||||
for (let i = 0; i < 12 * 5; i++) {
|
||||
if (i % 15 === 0)
|
||||
drawCircle(degToRad(i / (12 * 5) * 360 - 90), clockRadius - NOTCH_BUFFER - 4, 8, COLOR_MINUTE_HAND)
|
||||
else if (i % 5 === 0 && theme.notches >= 2)
|
||||
drawCircle(degToRad(i / (12 * 5) * 360 - 90), clockRadius - NOTCH_BUFFER - 4, 6, COLOR_LIGHT_OVERLAY)
|
||||
else if (theme.notches >= 3)
|
||||
drawCircle(degToRad(i / (12 * 5) * 360 - 90), clockRadius - NOTCH_BUFFER - 4, 4, COLOR_LIGHT_DIM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.events) {
|
||||
for (let event of events) {
|
||||
drawEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
drawCircle(0, 0, 36, COLOR_LIGHT_OVERLAY);
|
||||
drawCircle(0, 0, 20, '#fff');
|
||||
|
||||
if (theme.minutes) {
|
||||
const minuteAngle = degToRad((time.getMinutes() + time.getSeconds() / 60) / 60 * 360 - 180);
|
||||
drawRoundedRect(34, clockRadius - 64, minuteAngle, 16, COLOR_MINUTE_HAND, undefined, undefined);
|
||||
}
|
||||
|
||||
const hourAngle = degToRad(((time.getHours() % 12) + time.getMinutes() / 60) / 12 * 360 - 180);
|
||||
drawRoundedRect(36, clockRadius - 80, hourAngle, 16, 'rgba(0, 0, 0, 0.15)', '#fff', 4);
|
||||
|
||||
if (!ambient) setTimeout(() => window.requestAnimationFrame(drawWatchFace), 1000/10);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(drawWatchFace);
|
||||
|
||||
window.addEventListener('timetick', drawWatchFace);
|
||||
|
||||
window.addEventListener('ambientmodechanged', (e: any) => {
|
||||
ambient = e.detail.ambientMode;
|
||||
drawWatchFace();
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) drawWatchFace();
|
||||
});
|
||||
}
|
||||
new Watch(artist);
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
/**
|
||||
* Specifies how the notches should be rendered.
|
||||
*/
|
||||
|
||||
export const NotchMode = {
|
||||
NONE: 0,
|
||||
QUARTER: 1,
|
||||
HOURS: 2,
|
||||
MINUTES: 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme preferences that determine how the watch should be rendered.
|
||||
*/
|
||||
|
||||
export interface Theme {
|
||||
notchMode: number;
|
||||
showEvents: boolean;
|
||||
showMinutes: boolean;
|
||||
showGlow: boolean;
|
||||
glowColors: [ string, string, string ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theme preferences set by the user.
|
||||
*/
|
||||
|
||||
export function getTheme(): Theme {
|
||||
return {
|
||||
notchMode: NotchMode.MINUTES,
|
||||
showEvents: true,
|
||||
showMinutes: true,
|
||||
showGlow: true,
|
||||
glowColors: [
|
||||
'#4294ff',
|
||||
'rgba(5, 124, 242, 0.6)',
|
||||
'rgba(198, 0, 237, 1)'
|
||||
]
|
||||
};
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
/**
|
||||
* Converts the degree angle provided to radians.
|
||||
*
|
||||
* @param deg - The angle in degrees.
|
||||
* @returns - The same angle in radians.
|
||||
*/
|
||||
|
||||
export function degToRad(deg: number) {
|
||||
return deg * (Math.PI / 180);
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
import Artist from './Artist';
|
||||
import { degToRad } from './Util';
|
||||
import { getEvents, Event } from './Events';
|
||||
import { getTheme, NotchMode, Theme } from './Theme';
|
||||
|
||||
// TODO: placeholders
|
||||
const COLOR_LIGHT_DIM = 'rgba(65, 199, 232, 0.15)';
|
||||
const COLOR_LIGHT_OVERLAY = 'rgba(65, 199, 232, 0.33)';
|
||||
const COLOR_MINUTE_HAND = 'rgba(65, 199, 232, 0.8)';
|
||||
|
||||
/** The minimum distance that all elements should be from the screen edge. */
|
||||
const OUTER_BUFFER = 2;
|
||||
|
||||
/** The distance that elements should be away from the screen edge in addition to OUTER_BUFFER if notches are drawn. */
|
||||
const NOTCH_BUFFER = 12;
|
||||
|
||||
/** The text size of the date. */
|
||||
const DATE_SIZE = 20;
|
||||
|
||||
/**
|
||||
* Handles app events, drawing using the artist, ambient mode, and watch <-> companion communication.
|
||||
*/
|
||||
|
||||
export default class Watch {
|
||||
private theme: Theme;
|
||||
private events: Event[];
|
||||
|
||||
private ambient: boolean;
|
||||
private animStep: number;
|
||||
private animReq?: number;
|
||||
private animTimeout?: number;
|
||||
|
||||
constructor(private artist: Artist) {
|
||||
this.animStep = 0;
|
||||
this.ambient = false;
|
||||
this.theme = getTheme();
|
||||
this.events = getEvents();
|
||||
|
||||
/** Triggered only in ambient mode. */
|
||||
window.addEventListener('timetick', () => this.draw());
|
||||
|
||||
/** Update variables and redraw when the ambient mode changes. */
|
||||
window.addEventListener('ambientmodechanged', (e: any) => {
|
||||
const ambient = e.detail.ambientMode;
|
||||
if (this.ambient === ambient) return;
|
||||
this.ambient = ambient;
|
||||
this.draw();
|
||||
});
|
||||
|
||||
/** Immediately rerender when the visibility state chanegs. */
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) this.draw();
|
||||
});
|
||||
|
||||
this.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears pending draws.
|
||||
*/
|
||||
|
||||
private clearPendingDraw() {
|
||||
if (this.animTimeout !== undefined) window.clearTimeout(this.animTimeout);
|
||||
this.animTimeout = undefined;
|
||||
if (this.animReq !== undefined) window.cancelAnimationFrame(this.animReq);
|
||||
this.animReq = undefined;
|
||||
}
|
||||
|
||||
private getTime() {
|
||||
try { return tizen.time.getCurrentDateTime(); }
|
||||
catch (e) { return new Date(); }
|
||||
}
|
||||
|
||||
private draw() {
|
||||
const { artist, theme, events } = this;
|
||||
|
||||
this.clearPendingDraw();
|
||||
artist.clear();
|
||||
|
||||
const time = this.getTime();
|
||||
|
||||
artist.ctx.font = `900 ${DATE_SIZE}px Arial sans-serif`;
|
||||
artist.ctx.fillStyle = '#aaa';
|
||||
artist.ctx.textBaseline = 'bottom';
|
||||
artist.ctx.textAlign = 'center';
|
||||
|
||||
artist.ctx.fillText(`${['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'][time.getDay()]} ${time.getDate()}`,
|
||||
artist.radius, artist.radius + artist.radius / 1.75);
|
||||
|
||||
artist.ctx.fillStyle = '#666';
|
||||
artist.ctx.fillRect(artist.radius - 1 * DATE_SIZE,
|
||||
artist.radius + artist.radius / 1.75 + 2, 2 * DATE_SIZE, 2);
|
||||
|
||||
if (!this.ambient && theme.showGlow) {
|
||||
this.animStep = (this.animStep + 1) % 360;
|
||||
|
||||
let scaleA = 0.8 + (Math.sin(degToRad(this.animStep + 180)) / 6);
|
||||
let scaleB = 0.8 + (Math.cos(degToRad(this.animStep)) / 6);
|
||||
let scaleC = 1.2 + (Math.sin(degToRad((this.animStep * 3) + 90)) / 6);
|
||||
let offset = Math.cos(degToRad(this.animStep * 3)) * 50;
|
||||
|
||||
artist.ctx.globalCompositeOperation = 'lighter';
|
||||
artist.glow(degToRad(this.animStep), offset, scaleA, theme.glowColors[1]);
|
||||
artist.glow(0, 0, scaleC, theme.glowColors[0]);
|
||||
artist.glow(degToRad(this.animStep + 180), offset, scaleB, theme.glowColors[2]);
|
||||
artist.ctx.globalCompositeOperation = 'source-over';
|
||||
}
|
||||
|
||||
if (theme.notchMode !== NotchMode.NONE) {
|
||||
for (let i = 0; i < 12 * 5; i++) {
|
||||
if (i % 15 === 0) {
|
||||
artist.circle(degToRad(i / (12 * 5) * 360),
|
||||
artist.radius - OUTER_BUFFER - 4, 4, COLOR_MINUTE_HAND);
|
||||
}
|
||||
else if (i % 5 === 0 && theme.notchMode >= NotchMode.HOURS) {
|
||||
artist.circle(degToRad(i / (12 * 5) * 360 - 90),
|
||||
artist.radius - OUTER_BUFFER - 4, 3, COLOR_LIGHT_OVERLAY);
|
||||
}
|
||||
else if (theme.notchMode >= NotchMode.MINUTES) {
|
||||
artist.circle(degToRad(i / (12 * 5) * 360 - 90),
|
||||
artist.radius - OUTER_BUFFER - 4, 2, COLOR_LIGHT_DIM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.showEvents) {
|
||||
for (let event of events) {
|
||||
let eventBuffer = OUTER_BUFFER + theme.notchMode !== NotchMode.NONE ? NOTCH_BUFFER : 0;
|
||||
artist.event(event, artist.radius - eventBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
artist.circle(0, 0, 18, COLOR_LIGHT_OVERLAY);
|
||||
artist.circle(0, 0, 10, '#fff');
|
||||
|
||||
if (theme.showMinutes) {
|
||||
const minuteAngle = degToRad((time.getMinutes() + time.getSeconds() / 60) / 60 * 360 - 180);
|
||||
artist.rounded(minuteAngle, 40, artist.radius - 64, 16, COLOR_MINUTE_HAND);
|
||||
}
|
||||
|
||||
const hourAngle = degToRad(((time.getHours() % 12) + time.getMinutes() / 60) / 12 * 360 - 180);
|
||||
artist.rounded(hourAngle, 42, artist.radius - 80, 16, 'rgba(0, 0, 0, 0.15)', '#fff', 4);
|
||||
|
||||
if (!this.ambient) this.animTimeout = setTimeout(() =>
|
||||
this.animReq = window.requestAnimationFrame(() => this.draw()),
|
||||
1000/10) as any;
|
||||
}
|
||||
}
|
|
@ -3,13 +3,16 @@
|
|||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
|
||||
"target": "es5",
|
||||
"outFile": "build/main.js",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"lib": [ "dom", "es2015" ],
|
||||
|
||||
"typeRoots": [ "./node_modules/@types/" ],
|
||||
|
||||
"noEmitHelpers": true,
|
||||
"importHelpers": true,
|
||||
"removeComments": true,
|
||||
"noUnusedLocals": true,
|
||||
"noImplicitReturns": true,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
1.0.5
|
|
@ -0,0 +1,53 @@
|
|||
import ForkTsCheckerPlugin from 'fork-ts-checker-webpack-plugin';
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
mode: 'production',
|
||||
stats: 'errors-warnings',
|
||||
|
||||
entry: { main: './src/Main.ts' },
|
||||
resolve: {
|
||||
extensions: [ '.ts', '.js' ]
|
||||
},
|
||||
output: {
|
||||
path: __dirname + '/build',
|
||||
filename: 'main.js'
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new ForkTsCheckerPlugin({
|
||||
typescript: {
|
||||
configFile: './tsconfig.json',
|
||||
},
|
||||
eslint: {
|
||||
files: './src/**/*.ts',
|
||||
options: {
|
||||
configFile: './.eslintrc.js',
|
||||
emitErrors: true,
|
||||
failOnHint: true,
|
||||
typeCheck: true
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.[t|j]s$/,
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
babelrc: false,
|
||||
cacheDirectory: true,
|
||||
presets: [
|
||||
[ '@babel/preset-typescript' ],
|
||||
[ '@babel/preset-env', { targets: { browsers: [ 'Chrome 90' ]} }]
|
||||
],
|
||||
plugins: [
|
||||
// ['@babel/transform-react-jsx', { pragma: 'h' }],
|
||||
// ['@babel/plugin-proposal-class-properties', { loose: true }],
|
||||
// ['@babel/plugin-proposal-private-methods', { loose: true }]
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue