From 63357cf82b009fad9fef6d20d9b5ec1e96ad498d Mon Sep 17 00:00:00 2001 From: linarphy Date: Tue, 30 Jan 2024 23:31:36 +0100 Subject: [PATCH] Add theming & Update README.md --- README.md | 1 + lichartee.js | 545 ++++++++++++++++++++++++++++++++++----------------- main.css | 28 +++ 3 files changed, 398 insertions(+), 176 deletions(-) diff --git a/README.md b/README.md index 62e8122..55c847c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ This project is licensied under the GPL 3 license - see the *v 0.1.6* - Add i18n feature - Add named argument +- Add theming *v 0.1.5* - Add state interface with jsplot (pyplot procedural style) diff --git a/lichartee.js b/lichartee.js index fe64455..9814325 100644 --- a/lichartee.js +++ b/lichartee.js @@ -1,3 +1,130 @@ +/** + * font object + * + * @property size int font-size (px) + * @property family string font-family + * @property color string font color + */ +class Font +{ + /** + * @param size int font-size (px) + * @param family string font-family + * @param color string font color + */ + constructor({ + size , + family, + color , + }) + { + this.size = size; + this.family = family; + this.color = color; + } + + /** + * Convert font to css font proprety's syntax + * + * @return String + */ + toString() + { + return this.size.toString() + 'px ' + self.family; + } +} + +var LightTheme = { + line : { + style: 'solid', + color: 'black', + } , + marker: { + style: '' , + color: 'black', + size : 5 , + } , + title : { + font : new Font({ + size : 20 , + family: 'sans-serif', + color : 'black' , + }) , + margin: 10, + } , + bordercolor: 'black' , + axis : { + font : new Font({ + size : 15 , + family: 'sans-serif', + color : 'black' , + }) , + ticks : { + x: { + number: 4, + } , + y: { + number: 4, + } , + size : 5, + } , + margin: 15, + } , +}; + +var DarkTheme = { + line : { + style: 'solid', + color: 'white', + } , + marker: { + style: '' , + color: 'white', + size : 5 , + } , + title : { + font : new Font({ + size : 20 , + family: 'sans-serif', + color : 'white' , + }) , + margin: 10, + } , + bordercolor: 'white' , + axis : { + font : new Font({ + size : 15 , + family: 'sans-serif', + color : 'white' , + }) , + ticks : { + x: { + number: 4, + } , + y: { + number: 4, + } , + size : 5, + } , + margin: 15, + } , +}; + +var DefaultTheme = LightTheme; + +if ( + window.matchMedia( + '(prefers-color-scheme: dark)' + ).matches +) +{ + DefaultTheme = DarkTheme; +} +else +{ + DefaultTheme = LightTheme; +} + /** * a manager allows to manipulate multiple canvas * @@ -6,6 +133,7 @@ * * @property Element parent_element DOM element containing every canvas * of this manager + * @property Theme theme Theme of managed canvas * @property 1darray list_canvas List of canvas held by this * manager */ @@ -13,10 +141,15 @@ class Manager { /** * @param Element parent_element Element holding the canvas + * @param Theme theme Theme of managed canvas */ - constructor(parent_element) + constructor({ + parent_element , + theme = DefaultTheme, + }) { this.parent_element = parent_element; + this.theme = theme; this.list_canvas = []; } @@ -34,11 +167,11 @@ class Manager }) { let element_canvas = document.createElement('canvas'); - let text = document.createTextNode( + let text = document.createTextNode(_( 'Your browser doesn\'t seem to support canvas, or you ' + 'deactivated them. This is the canvas where the chart ' + 'should be displayed' - ); + )); element_canvas.appendChild(text); if (this.parent_element.childElementCount === 0) { @@ -54,7 +187,12 @@ class Manager ctx.canvas.width = width ; ctx.canvas.height = height; let figure = new Figure({}); - let canvas = new Canvas(ctx, figure); + let canvas = new Canvas({ + manager: this , + context: ctx , + figure : figure, + }); + canvas.figure.canvas = canvas; this.list_canvas.splice(position, 0, canvas); return canvas; } @@ -85,19 +223,22 @@ class Manager /** * a canvas allows to render a figure * + * @property Manager manager Manager of this canvas * @property CanvasRenderingContext2D ctx Canvas API interface * @property Figure figure Figure held by this canvas */ class Canvas { /** + * @param Manager manager Manager of this canvas * @param CanvasRenderingContext2D ctx Canvas API interface * @param Figure figure Figure held by this canvas */ - constructor(ctx, figure) + constructor({manager, context , figure}) { - this.ctx = ctx ; - this.figure = figure; + this.manager = manager; + this.ctx = context; + this.figure = figure ; } /** @@ -109,19 +250,25 @@ class Canvas { throw _('no_figure') } + this.ctx.clearRect( + 0 , + 0 , + this.ctx.canvas.width , + this.ctx.canvas.height, + ); let start_x = 0; for (const index_axes in this.figure.list_axes) { let axes = this.figure.list_axes[index_axes]; let x_proportion = axes.width/this.figure.width; let end_x = start_x + this.ctx.canvas.width * x_proportion; - axes.draw( - this.ctx, - [ + axes.draw({ + ctx: this.ctx, + box: [ [start_x, 0 ], [end_x , this.ctx.canvas.height], - ], - ); + ] , + }); start_x = end_x; } } @@ -130,6 +277,7 @@ class Canvas /** * a figure hold axes and every plot elements * + * @property Canvas canvas Canvas of this figure * @property int width Total width of this figure (related to each * axes) * @property int height Total height of this figure (related to each @@ -138,11 +286,12 @@ class Canvas */ class Figure { - constructor() + constructor({}) { - this.width = 0 ; - this.height = 0 ; - this.list_axes = []; + this.canvas = undefined; + this.width = 0 ; + this.height = 0 ; + this.list_axes = [] ; } /** @@ -161,7 +310,8 @@ class Figure this.width += relative_width ; this.height += relative_height; let axes = new Axes({ - width: relative_width , + figure: this , + width : relative_width , height: relative_height, }); this.list_axes.splice(position, 0, axes); @@ -173,7 +323,7 @@ class Figure * * @param int position Position of the axes in the figure to delete */ - remove_axes(position = 0) + remove_axes({position = 0}) { this.list_axes.splice(position, 1); } @@ -182,6 +332,7 @@ class Figure /** * an axes represents one plot in a figure. * + * @property Figure figure Figure of this Axes * @property int width Width of the axes * @property int height Height of the axes * @property String title Title of the axes @@ -190,19 +341,23 @@ class Figure class Axes { /** + * @param Figure figure Figure of this Axes * @param int width Width of the axes * @param int height Height of the axes - * @param String title Title of the axes */ constructor({ + figure , width = 100, height = 100, }) { - this.width = width; - this.height = height; - this.axis = new Axis({}); - this.lines = []; + this.figure = figure; + this.width = width ; + this.height = height; + this.axis = new Axis({ + axes: this, + }) ; + this.lines = [] ; } /** @@ -224,23 +379,24 @@ class Axes * @param Number markersize size of the marker */ plot({ - x , - y , - linestyle = 'solid' , - linecolor = 'black' , - markerstyle = '' , - markercolor = 'black' , - markersize = 5 , + x , + y , + linestyle , + linecolor , + markerstyle, + markercolor, + markersize , }) { let line = new Line({ - x : x , - y : y , - linestyle : linestyle , - linecolor : linecolor , - markerstyle: markerstyle, - markercolor: markercolor, - markersize : markersize , + axes : this , + x : x , + y : y , + linestyle : linestyle , + linecolor : linecolor , + markerstyle: markerstyle , + markercolor: markercolor , + markersize : markersize , }); this.lines.push(line); } @@ -249,9 +405,9 @@ class Axes * render the axes to the canvas * * @param ctx CanvasRenderingContext2D Context of the canvas - * @param view 2darray Window where to draw the canvas + * @param box 2darray Window where to draw the canvas */ - draw(ctx, view) + draw({ctx, box}) { const x_min = get_ext_array( this.lines.map( @@ -289,37 +445,37 @@ class Axes ) ) ); - let n_view = view; + let n_box = box; if (this.title !== undefined) { - n_view = this.title.draw( - ctx , - view, - ); + n_box = this.title.draw({ + ctx: ctx, + box: box, + }); } - n_view = this.axis.draw( - ctx , - n_view, - [ [x_min, y_min], [x_max, y_max] ], - ); + n_box = this.axis.draw({ + ctx : ctx , + box : n_box , + view : [ [x_min, y_min], [x_max, y_max] ], + }); for (const index_line in this.lines) { - this.lines[index_line].draw( - ctx , - n_view, - [ + this.lines[index_line].draw({ + ctx : ctx , + box : n_box , + view: [ [x_min, y_min], [x_max, y_max], - ] , - ); + ] , + }); } ctx.beginPath(); - ctx.strokeStyle = 'black'; + ctx.strokeStyle = this.figure.canvas.manager.theme.bordercolor; ctx.setLineDash([]); ctx.strokeRect( - n_view[0][0], n_view[0][1], - n_view[1][0] - n_view[0][0], - n_view[1][1] - n_view[0][1], + n_box[0][0], n_box[0][1], + n_box[1][0] - n_box[0][0], + n_box[1][1] - n_box[0][1], ); } @@ -330,7 +486,10 @@ class Axes */ set title(content) { - this._title = new Title({content: content}); + this._title = new Title({ + axes : this , + content: content, + }); } /** @@ -345,6 +504,7 @@ class Axes /** * a line, with a color and a line style * + * @property Axes axes Axes of this line * @property 1darray x xdata * @property 1darray y ydata * @property String linestyle style of the line (can be "solid", @@ -363,31 +523,59 @@ class Axes class Line { /** + * @param Axes axes Axes of this Line * @param 1darray x xdata * @param 1darray y ydata - * @param string linestyle style of the line (can be "solid", + * @param String linestyle style of the line (can be "solid", * "dotted", "dashdot", "dashed" or "" * for no line) - * @param string linecolor color of the line (can be "#abcdef", + * @param String linecolor color of the line (can be "#abcdef", * "rgb(x,y,z)" or a name of a color. - * @param string markerstyle style of the marker used for each + * @param String markerstyle style of the marker used for each * point (can be "circle", "point", * "small", "cross" or "" for no * marker) - * @param string markercolor color of the marker (same + * @param String markercolor color of the marker (same * definition than linecolor) * @param Number markersize size of the marker */ constructor({ - x , - y , - linestyle = 'solid' , - linecolor = 'black' , - markerstyle = '' , - markercolor = 'black' , - markersize = 5 , + axes , + x , + y , + linestyle , + linecolor , + markerstyle, + markercolor, + markersize , }) { + this.axes = axes ; + if (linestyle === undefined) + { + linestyle = this.axes.figure.canvas.manager.theme.line + .style; + } + if (linecolor === undefined) + { + linecolor = this.axes.figure.canvas.manager.theme.line + .color; + } + if (markerstyle === undefined) + { + markerstyle = this.axes.figure.canvas.manager.theme.marker + .style; + } + if (markercolor === undefined) + { + markercolor = this.axes.figure.canvas.manager.theme.marker + .color; + } + if (markersize === undefined) + { + markersize = this.axes.figure.canvas.manager.theme.marker + .size; + } this.x = x ; this.y = y ; this.linestyle = linestyle ; @@ -405,7 +593,7 @@ class Line * @param 2dlist view scale of the box (link value <-> * coordinate */ - draw(ctx, box, view) + draw({ctx, box, view}) { const x_delta = view[1][0] - view[0][0]; const y_delta = view[1][1] - view[0][1]; @@ -432,19 +620,19 @@ class Line ); if (this.linestyle != '') { - this.drawLine( - ctx, - x_coordinates, - y_coordinates, - ); + this.drawLine({ + ctx : ctx , + x_coordinates: x_coordinates, + y_coordinates: y_coordinates, + }); } if (this.markerstyle != '') { - this.drawPoints( - ctx , - x_coordinates, - y_coordinates, - ); + this.drawPoints({ + ctx : ctx , + x_coordinates: x_coordinates, + y_coordinates: y_coordinates, + }); } } @@ -455,7 +643,7 @@ class Line * @param 1darray x_coordinates X coordinates of the point * @param 1darray y_coordinates Y coordinates of the point */ - drawLine(ctx, x_coordinates, y_coordinates) + drawLine({ctx, x_coordinates, y_coordinates}) { ctx.beginPath(); ctx.strokeStyle = this.linecolor; @@ -500,7 +688,7 @@ class Line * @param 1darray x_coordinates x coordinate of the point * @param 1darray y_coordinates y coordinate of the point */ - drawPoints(ctx, x_coordinates, y_coordinates) + drawPoints({ctx, x_coordinates, y_coordinates}) { for (const index in x_coordinates) { @@ -581,67 +769,41 @@ class Line } } -/** - * font object - * - * @property size int font-size (px) - * @property family string font-family - * @property color string font color - */ -class Font -{ - /** - * @param size int font-size (px) - * @param family string font-family - * @param color string font color - */ - constructor({ - size = 20 , - family = 'sans-serif', - color = 'black' , - }) - { - this.size = size; - this.family = family; - this.color = color; - } - - /** - * Convert font to css font proprety's syntax - * - * @return String - */ - toString() - { - return this.size.toString() + 'px ' + self.family; - } -} - /** * title of an axes * - * @property content String Content of a title - * @property font Font Font of a title + * @property Axes axes Axes of this title + * @property String content Content of the title + * @property Font font Font of the title + * @property Number margin Margin of the title */ class Title { /** - * @param content Content Content of a title - * @param font Font Font of a title + * @param Axes axes Axes of this title + * @param String content Content of the title + * @param Font font Font of the title + * @param Number margin Margin of the title */ constructor({ - content = '' , - font = undefined, - margin = 5 , + axes , + content , + font , + margin , }) { - this.content = content; - if (font === undefined) + this.axes = axes ; + if (font === undefined) { - font = new Font({}); + font = this.axes.figure.canvas.manager.theme.title.font; } - this.font = font; - this.margin = margin; + if (margin === undefined) + { + margin = this.axes.figure.canvas.manager.theme.title.margin; + } + this.content = content; + this.font = font ; + this.margin = margin ; } /** @@ -652,12 +814,11 @@ class Title * * @return plot_box int New windows for the plot */ - draw(ctx, box) + draw({ctx, box}) { - ctx.font = this.font.toString(); - ctx.fillStyle = 'black'; - ctx.strokeStyle = 'black'; - let width = ctx.measureText(this.content).width; + ctx.font = this.font.toString(); + ctx.fillStyle = this.font.color; + let width = ctx.measureText(this.content).width; if (width > box[1][0] - box[0][0] + 2 * this.margin) { ctx.fillText( @@ -691,41 +852,71 @@ class Title /** * Axis (x and y) of the axes (not separated xaxis and yaxis) * - * @property int number_x_tick Number of x axis tick - * @property int number_y_tick Number of y axis tick - * @property int size_tick Size of the tick - * @property int margin Size of the margin + * @property Axes axes Axes of the Axis + * @property Number number_x_tick Number of x axis tick + * @property Number number_y_tick Number of y axis tick + * @property Number size_tick Size of the tick + * @property Number margin Size of the margin * @property Font font Font of tick labels */ class Axis { /** - * @param number_x_tick int Number of x axis tick - * @param number_y_tick int Number of y axis tick - * @param size_tick int Size of the tick - * @param margin int Size of the margin - * @param text_height int Height of the text label + * @param Axes axes Axes of the Axis + * @param Number number_x_tick Number of x axis tick + * @param Number number_y_tick Number of y axis tick + * @param Number size_tick Size of the tick + * @param Number margin Size of the margin + * @param Number text_height Height of the text label * @param Font font Font of tick labels */ constructor({ - number_x_tick = 4 , - number_y_tick = 4 , - size_tick = 5 , - margin = 15 , - text_height = 15 , - font = undefined, + axes , + number_x_tick, + number_y_tick, + size_tick , + margin , + text_height , + font , }) { + this.axes = axes ; + if (number_x_tick === undefined) + { + number_x_tick = this.axes.figure.canvas.manager.theme.axis + .ticks.x.number; + } + if (number_y_tick === undefined) + { + number_y_tick = this.axes.figure.canvas.manager.theme.axis + .ticks.y.number; + } + if (size_tick === undefined) + { + size_tick = this.axes.figure.canvas.manager.theme.axis + .ticks.size; + } + if (margin === undefined) + { + margin = this.axes.figure.canvas.manager.theme.axis + .margin; + } + if (text_height === undefined) + { + text_height = this.axes.figure.canvas.manager.theme.axis + .font.size; + } + if (font === undefined) + { + font = this.axes.figure.canvas.manager.theme.axis + .font; + } this.number_x_tick = number_x_tick; this.number_y_tick = number_y_tick; - this.size_tick = size_tick; - this.margin = margin; - this.text_height = text_height; - if (font === undefined) - { - font = new Font({}); - } - this.font = font; + this.size_tick = size_tick ; + this.margin = margin ; + this.text_height = text_height ; + this.font = font ; } /** @@ -737,18 +928,18 @@ class Axis * real value * @return 2darray New window where to draw the rest of the plot */ - draw(ctx, box, view) + draw({ctx, box, view}) { - ctx.font = this.font.toString(); - ctx.strokeStyle = 'black'; - ctx.fillStyle = 'black'; - let index = 0; + ctx.font = this.font.toString(); + ctx.strokeStyle = this.font.color; + ctx.fillStyle = this.font.color; + let index = 0; let start_pos = box[0][1]; - let padding = ( + let padding = ( box[1][1] - box[0][1] - this.margin - this.text_height - this.size_tick ) / this.number_y_tick; - let step = (view[1][1] - view[0][1]) / this.number_y_tick; - let width = 2 * this.text_height; + let step = (view[1][1] - view[0][1]) / this.number_y_tick; + let width = 2 * this.text_height; while (index <= this.number_y_tick) { let current_y = start_pos + index * padding; @@ -824,12 +1015,14 @@ class jsplot { /** * @param Manager manager Current managed manager + * @param Theme theme Theme for managed canvas */ - constructor() + constructor(theme) { - this.manager = new Manager( - document.getElementsByTagName('main')[0] - ); + this.manager = new Manager({ + parent_element: document.getElementsByTagName('main')[0], + theme : theme , + }); } /** @@ -916,14 +1109,14 @@ class jsplot if (this.manager.list_canvas.length === 0) { this.manager.add_canvas({ - width: 500 , - height: 500 , - position: this.manager.list_canvas - 1, + width: 500, + height: 500, + position: 0 , }); } else { - this.manager.list_canvas[0].draw(); + this.manager.draw(); this.manager.add_canvas({ width: 500 , height: 500 , diff --git a/main.css b/main.css index 8b13789..273e179 100644 --- a/main.css +++ b/main.css @@ -1 +1,29 @@ +:root +{ + --background-color: white; + --color : black; +} +@media (prefers-color-scheme: light) +{ + :root + { + --background-color: white; + --color : black; + } +} + +@media (prefers-color-scheme: dark) +{ + :root + { + --background-color: black; + --color : white; + } +} + +body +{ + background-color: var(--background-color); + color : var(--color ); +}