Canvas-centered UI components for building rich browser editors, map-like visual tools, and candlestick/technical-indicator stock charts.
observableevent stream (from@dooboostore/core)../bundle-entrysubpath for bundlers or UMD/ESM interop workflows.@dooboostore/lib-webroot currently exposes:
ImageEditorCanvasGpsMarkerCanvasImageCropCanvasTradeChartOverlayStockChart(plus their associated types, e.g.CropCanvasConfig,CropCanvasRunParameter,GpsMarkerCanvasConfig,MarkerPoint,PathData,ImageCropCanvasConfig,CropResult,ChartConfig,ChartDataPoint,OHLCV,Transaction,TickerData,LineType,NormalizeType,EventMarker, and theOverlayStockChartevent type guards.)
import{ImageEditorCanvas,GpsMarkerCanvas,ImageCropCanvas,TradeChart,OverlayStockChart}from'@dooboostore/lib-web';import{ImageEditorCanvas}from'@dooboostore/lib-web/bundle-entry';bundle-entryalso importsreflect-metadataand re-exports the peer packages as namespaces:
import{ImageEditorCanvas,Core,CoreWeb}from'@dooboostore/lib-web/bundle-entry';// Core = @dooboostore/core, CoreWeb = @dooboostore/core-webThe root also has a default export object{ GpsMarkerCanvas, ImageCropCanvas, ImageEditorCanvas, TradeChart, OverlayStockChart }.
# pnpmpnpm add @dooboostore/lib-web# npmnpm install @dooboostore/lib-web# yarnyarn add @dooboostore/lib-webImageEditorCanvastakes the owningWindowas its first argument and a config with acanvas(anHTMLCanvasElement, or a CSS selector string resolved viaconfig.canvas) as the second. It sizes itself from the canvas element's ownclientWidth/clientHeight— there's no separatewidth/heightoption.
import{ImageEditorCanvas}from'@dooboostore/lib-web';consteditor =newImageEditorCanvas(window, {canvas:'#app',// or an HTMLCanvasElementonSelectionChange:(layer) =>console.log('selected:', layer),onCuttingModeChange:(isActive, targetLayer) =>console.log('crop/erase mode:', isActive, targetLayer),onHistoryChange:(canUndo, canRedo) =>console.log('history:', canUndo, canRedo),
});// Add layersawaiteditor.addImage(someBlobOrImageBitmap);awaiteditor.addText('Hello', {font:'sans-serif',size:48,fillColor:'#000'});awaiteditor.addRect({fillColor:'#fff',strokeColor:'#000',strokeWidth:2});// ...also: addCircle / addTriangle / addStar / addHeart / addSpeechBubble / addPentagon / addOctagon / addSemicircle / addCross// Manipulate the currently-selected layereditor.bringSelectedLayerToFront();
editor.sendSelectedLayerToBack();
editor.enterEraseMode();// crop-out part of the selected layerawaiteditor.updateSelectedText('New text');// Historyeditor.undo();
editor.redo();// Resize the underlying canvaseditor.resize(1024,768);// Export the composited resultconstdataUrl =awaiteditor.exportFinalImage();Also:updateSelectedText(Options),updateSelected<Shape>(options),getSelectedLayer(),getMode()('none' | 'crop' | 'erase'),isSelectedMode(). Layers can also be added viaeditor.run({ img, handle, cropStroke })sinceImageEditorCanvasimplementsRunnable<void, CropCanvasRunParameter>from@dooboostore/core.
GpsMarkerCanvastakes an existingHTMLCanvasElementand tracks its size with an internalResizeObserver— give the canvas a size via CSS, not constructor options.
import{GpsMarkerCanvas}from'@dooboostore/lib-web';constcanvas =document.getElementById('map')asHTMLCanvasElement;constmap =newGpsMarkerCanvas({
canvas,useMercatorProjection:true,// false = simple equirectangular (lon,lat) projectionmarkerColor:'red',markerSize:32,// also: lineWidth, padding, labelFont, labelColor, labelOffset});
map.setPaths([{points: [{lat:37.5,lon:127.0}, {lat:37.6,lon:127.1}],color:'#2196F3',closePath:false}]);
map.setMarkers([{lat:37.5,lon:127.0,label:'Start',marker: {type:'color',color:'#4CAF50'} }]);
map.setUseMercatorProjection(false);
map.zoomIn();
map.zoomOut();
map.resetViewAndFitData();// reset pan/zoom and refit to current datamap.destroy();// remove listeners + disconnect the ResizeObserverImageCropCanvasalso takes an existingHTMLCanvasElement. The user clicks to add polygon points on the loaded image (at least 3), drags them to adjust, and taps a point's × handle to remove it.
import{ImageCropCanvas}from'@dooboostore/lib-web';constcropper =newImageCropCanvas({canvas:document.getElementById('crop')asHTMLCanvasElement,onDone:(result) =>{// result: { points: {x,y}[], dataUrl: string | null } — image-relative polygon points + cropped PNG data URLconsole.log(result.dataUrl);
},
});
cropper.loadImage('/path/to/image.png');// ...user clicks/drags to build a polygon on the canvas...cropper.done();// fires onDone with the cropped resultcropper.cancel();// clear the in-progress polygoncropper.destroy();import{TradeChart}from'@dooboostore/lib-web';constdata =TradeChart.createData(ohlcvInputs, { transactions,maPeriods: [5,20,60] });constchart =newTradeChart({canvas:document.getElementById('chart')asHTMLCanvasElement,initialConfig: {show: {volume:true,rsi:true,macd:true,movingAverages:true,goldenCross:true,deadCross:true},enableZoom:true,
},
});
chart.setConfig({show: {bollingerBands:true} });// merge in more config later, chainable (returns `this`)chart.observable.subscribe(evt=>console.log(evt));// zoom/pan/click events (@dooboostore/core Observable)chart.getViewState();// { startIndex, count }; setViewState(...) to restorechart.resize();
chart.destroy();TradeChart.createData(inputs, config)turns raw OHLCV rows into theChartDataPoint[]the chart consumes, computing moving averages / OBV / VOSC / RSI / MACD and golden/dead-cross state along the way. Data setters (setTitle,setTransactions,setCross,setMAPeriods,setIsGroup) anddraw()are also chainable. Mostshow*toggles (e.g.showVolumePercentageLines,showPriceBollingerBands,showBuyMarkers, ...) are fluent (this-returning) shortcuts for the equivalentsetConfig({ show: { ... } })call.
import{OverlayStockChart}from'@dooboostore/lib-web';constdataMap =newMap([
['AAPL', {data: {datas: aaplChartData,events: [] },color:'#2196F3'}],
['MSFT', {data: {datas: msftChartData },lineMode:'line-smooth'}],
]);constoverlay =newOverlayStockChart(document.getElementById('overlay')asHTMLCanvasElement,
dataMap,
{initialState: {showCandles:false,showGrid:true,showAverage: [] } }// also: commonEvents, config);
overlay.updateState({showGrid:false});// partial RenderState update; getState() reads itoverlay.setData(dataMap);// replace data (optional commonEvents)overlay.destroy();OverlayStockChartdraws several tickers' series on one canvas for comparison, normalizing their scales ('none' | 'rangeNormalize' | 'normalize') and supporting several line-drawing styles ('line' | 'line-smooth' | 'line-smooth-open/high/low/middle' | 'step-to' | 'step-from' | 'step-center') plus point/range event markers (XPointEvent/YPointEvent/XYPointEvent/XRangeEvent/YRangeEvent/XYRangeEvent, distinguished via the exportedisXPointEvent/isRangeEvent/etc. type guards).
<canvas>element itself;GpsMarkerCanvas/ImageCropCanvastrack size automatically viaResizeObserver, whileImageEditorCanvasreadsclientWidth/clientHeightonce at construction (callresize()after layout changes).exportFinalImage()) rather than per-frame snapshots.destroy()onGpsMarkerCanvas/ImageCropCanvaswhen done to disconnect theirResizeObserverand event listeners.@dooboostore/lib-web) as default; keep deep imports internal.Issue:Blurry canvas output on high-DPI displays
Solution:GpsMarkerCanvasalready scales bywindow.devicePixelRatiointernally; forImageEditorCanvas/ImageCropCanvas, align canvas internal size with device pixel ratio yourself if you need the same treatment.
Issue:Slow interaction with many objects
Solution:reduce redraw region, batch operations, and disable expensive effects while dragging.
Issue:Import path errors in consumers
Solution:use root path or@dooboostore/lib-web/bundle-entry, not internal source paths.
The detailed API documentation for each component is available on our documentation website.
This package is licensed under theMIT License.