diff --git a/.gitignore b/.gitignore index 0a10c454..edbf1a8f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,10 @@ npm-debug.log* # Docs .cache-loader build/ -# generated typedocs -docs/api +docs/.vitepress/dist +docs/.vitepress/cache +docs/public/register.bundle.esm.js +docs/public/utils.bundle.esm.js # Development .DS_Store diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs new file mode 100644 index 00000000..140bf74a --- /dev/null +++ b/docs/.vitepress/config.mjs @@ -0,0 +1,108 @@ +import { defineConfig } from 'vitepress'; +import process from 'node:process'; + +const docsVersion = "VERSION"; +const base = process.env.NODE_ENV === "development" ? '/chartjs-plugin-zoom/master/' : `/chartjs-plugin-zoom/${docsVersion}/`; + +// https://vitepress.dev/reference/site-config +export default defineConfig({ + title: "chartjs-plugin-zoom", + description: "A zoom and pan plugin for Chart.js >= 3.0.0", + head: [ + ['link', { rel: 'icon', href: base + 'favicon.ico' }], + ['link', { rel: 'stylesheet', href: base + 'styles.css' }], + ], + base, + outDir: '../dist/docs', + appearance: false, + themeConfig: { + // https://vitepress.dev/reference/default-theme-config + logo: '/hero.svg', + footer: { + message: 'Released under the MIT License', + copyright: 'Copyright © 2016-2024 chartjs-plugin-zoom contributors', + }, + nav: [ + { + text: docsVersion, + items: [ + { text: 'Development (master)', link: '/chartjs-plugin-zoom/master/' }, + { text: '2.x.x', link: '/chartjs-plugin-zoom/2.0.1/' }, + { text: '1.x.x', link: '/chartjs-plugin-zoom/1.3.0/' }, + ], + }, + { text: 'Home', link: '/' }, + { text: 'Guide', link: '/guide/integration' }, + { text: 'Samples', link: `/samples/basic` }, + { + text: 'Ecosystem', + ariaLabel: 'Community Menu', + items: [ + { text: 'Awesome', link: 'https://github.com/chartjs/awesome' }, + ] + }, + ], + + sidebar: [ + { + text: 'Guide', + collapsed: false, + items: [ + { text: 'Integration', link: '/guide/integration' }, + { text: 'Usage', link: '/guide/usage' }, + { text: 'Options', link: '/guide/options' }, + { text: 'Animations', link: '/guide/animations' }, + { text: 'Developers', link: '/guide/developers' }, + ], + }, + { + text: 'Samples', + collapsed: false, + items: [ + { text: 'Basic', link: '/samples/basic' }, + { + text: 'Wheel Zoom', + collapsed: true, + items: [ + { text: 'Category Scale', link: '/samples/wheel/category' }, + { text: 'Logarithmic Scale', link: '/samples/wheel/log' }, + { text: 'Time Scale', link: '/samples/wheel/time' }, + { text: 'Over Scale Mode', link: '/samples/wheel/over-scale-mode' }, + { text: 'Click to Zoom', link: '/samples/wheel/click-zoom' }, + ], + }, + { + text: 'Drag to Zoom', + collapsed: true, + items: [ + { text: 'Category Scale', link: '/samples/drag/category' }, + { text: 'Linear Scale', link: '/samples/drag/linear' }, + { text: 'Logarithmic Scale', link: '/samples/drag/log' }, + { text: 'Time Scale', link: '/samples/drag/time' }, + { text: 'Timeseries Scale', link: '/samples/drag/timeseries' }, + ], + }, + { text: 'API', link: '/samples/api' }, + { text: 'Fetch Data', link: '/samples/fetch-data' }, + { text: 'Pan Region', link: '/samples/pan-region' }, + ] + }, + ], + + socialLinks: [ + { icon: 'github', link: 'https://github.com/chartjs/chartjs-plugin-zoom' }, + ] + }, + vue: { + template: { + compilerOptions: { + isCustomElement: tag => tag.startsWith('playground-'), + } + } + }, + vite: { + optimizeDeps: { + exclude: ['playground-elements'], + } + } +}) diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts deleted file mode 100755 index b2ca381e..00000000 --- a/docs/.vuepress/config.ts +++ /dev/null @@ -1,170 +0,0 @@ -import * as path from 'path'; -import { DefaultThemeConfig, defineConfig, PluginTuple } from 'vuepress/config'; - -const docsVersion = "VERSION"; -const base = process.env.NODE_ENV === "development" ? '/chartjs-plugin-zoom/master/' : `/chartjs-plugin-zoom/${docsVersion}/`; - -export default defineConfig({ - title: 'chartjs-plugin-zoom', - description: 'A zoom and pan plugin for Chart.js >= 3.0.0', - theme: 'chartjs', - base, - dest: path.resolve(__dirname, '../../dist/docs'), - head: [ - ['link', {rel: 'icon', href: '/favicon.ico'}], - ], - plugins: [ - ['flexsearch'], - ['redirect', { - redirectors: [ - // Default sample page when accessing /samples. - {base: '/samples', alternative: ['basic']}, - ], - }], - [ - 'vuepress-plugin-typedoc', - { - entryPoints: ['../../src/index.ts'], - hideInPageTOC: true, - tsconfig: 'tsconfig.json', - sidebar: { - fullNames: true, - parentCategory: 'API', - }, - }, - ], - ['@simonbrunel/vuepress-plugin-versions', { - filters: { - suffix: (tag) => tag ? ` (${tag})` : '', - title: (v, vars) => window.location.href.includes('master') ? 'Development (master)' : v + (vars.tag ? ` (${vars.tag})` : ''), - }, - menu: { - text: '{{version|title}}', - items: [ - { - text: 'Documentation', - items: [ - { - text: 'Development (master)', - link: '/chartjs-plugin-zoom/master/', - target: '_self', - }, - { - type: 'versions', - text: '{{version}}{{tag|suffix}}', - link: '/chartjs-plugin-zoom/{{version}}/', - exclude: /^[0]\.[0-4]\./, - group: 'minor', - target: '_self', - } - ] - }, - { - text: 'Release notes (5 latest)', - items: [ - { - type: 'versions', - limit: 5, - target: '_blank', - group: 'patch', - link: 'https://github.com/chartjs/chartjs-plugin-zoom/releases/tag/v{{version}}' - } - ] - } - ] - }, - }], - ] as PluginTuple[], - chainWebpack: (config) => { - config.module - .rule('chart.js') - .include.add(path.resolve('node_modules/chart.js')).end() - .use('babel-loader') - .loader('babel-loader') - .options({ - presets: ['@babel/preset-env'] - }) - .end(); - config.merge({ - resolve: { - alias: { - // Hammerjs requires window, using ng-hammerjs instead - 'hammerjs': 'ng-hammerjs', - } - } - }); - }, - themeConfig: { - repo: 'chartjs/chartjs-plugin-zoom', - logo: '/favicon.ico', - lastUpdated: 'Last Updated', - searchPlaceholder: 'Search...', - editLinks: false, - docsDir: 'docs', - chart: { - imports: [ - ['scripts/register.js'], - ['scripts/defaults.js'], - ['scripts/utils.js', 'Utils'], - ] - }, - nav: [ - {text: 'Home', link: '/'}, - {text: 'Guide', link: '/guide/'}, - {text: 'API', link: '/api/'}, - {text: 'Samples', link: `/samples/`}, - { - text: 'Ecosystem', - ariaLabel: 'Community Menu', - items: [ - { text: 'Awesome', link: 'https://github.com/chartjs/awesome' }, - ] - } - ], - sidebar: { - '/guide/': [ - '', - 'integration', - 'usage', - 'options', - 'animations', - 'developers', - ], - '/samples/': [ - 'basic', - { - title: 'Wheel Zoom', - children: [ - 'wheel/category', - 'wheel/category-min-range', - 'wheel/log', - 'wheel/time', - 'wheel/over-scale-mode', - 'wheel/click-zoom', - ] - }, - { - title: 'Drag to Zoom', - children: [ - 'drag/category', - 'drag/linear-ratio', - 'drag/linear', - 'drag/log', - 'drag/reject-outside', - 'drag/time', - 'drag/timeseries', - ] - }, - { - title: 'Pan', - children: [ - 'pan/region', - 'pan/toggle', - ] - }, - 'fetch-data', - 'api', - ], - } as any - } as DefaultThemeConfig -}); diff --git a/docs/guide/index.md b/docs/guide/index.md deleted file mode 100644 index b1c177ab..00000000 --- a/docs/guide/index.md +++ /dev/null @@ -1,19 +0,0 @@ -# Getting Started - -A zoom and pan plugin for Chart.js >= 3.0.0 - -Panning can be done via the mouse or with a finger. -Zooming is done via the mouse wheel or via a pinch gesture. [Hammer.js](https://hammerjs.github.io/) is used for gesture recognition. - -## Installation - -```bash -> npm install chartjs-plugin-zoom -``` - -:::tip - -**Important Note:** For Chart.js 2.6.0 to 2.9.x support, use [version 0.7.7 of this plugin](https://github.com/chartjs/chartjs-plugin-zoom/releases/tag/v0.7.7). -Documentation for v0.7.7 can be found on [GitHub](https://github.com/chartjs/chartjs-plugin-zoom/blob/v0.7.7/README.md). - -::: diff --git a/docs/guide/usage-example.js b/docs/guide/usage-example.js new file mode 100644 index 00000000..3af0cad8 --- /dev/null +++ b/docs/guide/usage-example.js @@ -0,0 +1,40 @@ +/* playground-hide */ +import Chart from './register.js'; +/* playground-hide-end */ +const ctx = document.querySelector('canvas'); + +const chart = new Chart(ctx, { + type: 'line', + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], + datasets: [{ + label: 'My First Dataset', + data: [65, 59, 80, 81, 56, 55, 40], + fill: false, + borderColor: 'rgb(75, 192, 192)', + tension: 0.1 + }] + }, + options: { + plugins: { + zoom: { + zoom: { + wheel: { + enabled: true, + }, + pinch: { + enabled: true, + }, + mode: 'xy', + } + } + } + } +}); + +const btnResetZoom = document.createElement('button'); +btnResetZoom.textContent = 'Reset zoom'; +document.body.append(btnResetZoom); +btnResetZoom.addEventListener('click', () => { + chart.resetZoom(); +}) \ No newline at end of file diff --git a/docs/guide/usage.md b/docs/guide/usage.md index 33ff1880..3267d9bb 100644 --- a/docs/guide/usage.md +++ b/docs/guide/usage.md @@ -2,47 +2,12 @@ Using the zoom and pan plugin is very simple. Once the plugin is [registered](./integration) zoom options provided to the chart will be used. In this example, scroll zoom is enabled. -```js chart-editor -/* */ -const config = { - type: 'line', - data: { - labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], - datasets: [{ - label: 'My First Dataset', - data: [65, 59, 80, 81, 56, 55, 40], - fill: false, - borderColor: 'rgb(75, 192, 192)', - tension: 0.1 - }] - }, - options: { - plugins: { - zoom: { - zoom: { - wheel: { - enabled: true, - }, - pinch: { - enabled: true - }, - mode: 'xy', - } - } - } - } -} -/* */ +
-module.exports = { - actions: [ - { - name: 'Reset zoom', - handler: function(chart) { - chart.resetZoom() - } - } - ], - config -} -``` + diff --git a/docs/index.md b/docs/index.md index a5a41240..1a2e875c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,21 @@ ---- -home: true -heroImage: /hero.svg -actionText: Get Started → -actionLink: /guide/ -footer: MIT Licensed | Copyright © 2016-2021 chartjs-plugin-zoom contributors ---- +![logo](/hero.svg) + +# chartjs-plugin-zoom + +A zoom and pan plugin for Chart.js >= 3.0.0 + +Panning can be done via the mouse or with a finger. +Zooming is done via the mouse wheel or via a pinch gesture. [Hammer.js](https://hammerjs.github.io/) is used for gesture recognition. + +## Installation + +```bash +> npm install chartjs-plugin-zoom +``` + +:::tip + +**Important Note:** For Chart.js 2.6.0 to 2.9.x support, use [version 0.7.7 of this plugin](https://github.com/chartjs/chartjs-plugin-zoom/releases/tag/v0.7.7). +Documentation for v0.7.7 can be found on [GitHub](https://github.com/chartjs/chartjs-plugin-zoom/blob/v0.7.7/README.md). + +::: diff --git a/docs/.vuepress/public/favicon.ico b/docs/public/favicon.ico similarity index 100% rename from docs/.vuepress/public/favicon.ico rename to docs/public/favicon.ico diff --git a/docs/.vuepress/public/favicon.png b/docs/public/favicon.png similarity index 100% rename from docs/.vuepress/public/favicon.png rename to docs/public/favicon.png diff --git a/docs/.vuepress/public/hero.svg b/docs/public/hero.svg similarity index 100% rename from docs/.vuepress/public/hero.svg rename to docs/public/hero.svg diff --git a/docs/public/sample-styles.css b/docs/public/sample-styles.css new file mode 100644 index 00000000..65f7f313 --- /dev/null +++ b/docs/public/sample-styles.css @@ -0,0 +1,37 @@ +body { + margin: 0; +} + +canvas { + margin-block: 8px; +} + +button { + transition: background-color .25s,border-color .25s; + background: rgba(40,44,52,.05); + border: 1px solid transparent; + border-radius: 6px; + color: #3080d0; + text-decoration: none!important; + display: inline-block; + font-size: .8rem; + padding: 8px 16px; + margin-inline-end: 8px; + margin-block-end: 8px; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none +} + +button:hover { + background-color: rgba(48,128,208,.15); + border-color: rgba(48,128,208,.2); + color: #3080d0 +} + +button:active { + background-color: rgba(48,128,208,.3); + border-color: rgba(48,128,208,.4); + color: #3080d0 +} \ No newline at end of file diff --git a/docs/public/styles.css b/docs/public/styles.css new file mode 100644 index 00000000..e4490948 --- /dev/null +++ b/docs/public/styles.css @@ -0,0 +1,13 @@ +playground-file-editor { + background-color: var(--vp-code-block-bg, '#f6f6f7'); + --playground-code-background: var(--vp-code-block-bg, '#f6f6f7'); + height: max-content; + border-radius: 8px; + padding-block: 8px; +} + +@media (min-width: 640px) { + playground-code-editor { + border-radius: 8px; + } +} \ No newline at end of file diff --git a/docs/samples/api.js b/docs/samples/api.js new file mode 100644 index 00000000..ee606452 --- /dev/null +++ b/docs/samples/api.js @@ -0,0 +1,86 @@ +/* playground-hide */ +import Chart from '../scripts/register.js'; +import * as Utils from '../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const data = { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }, { + label: 'My Second dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scaleOpts = { + reverse: true, + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } +}; +const scales = { + x: { + position: 'top', + }, + y: { + position: 'right', + }, +}; +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +/* playground-fold-end */ + +// chart +/* playground-fold */ +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + } +}); +/* playground-fold-end */ + +// buttons +createButton('Zoom +10%', () => chart.zoom(1.1)); +createButton('Zoom -10%', () => chart.zoom(0.9)); +createButton('Zoom x +10%', () => chart.zoom({x: 1.1})); +createButton('Zoom x -10%', () => chart.zoom({x: 0.9})); +createButton('Pan x 100px (anim)', () => chart.pan({x: 100}, undefined, 'default')); +createButton('Pan x -100px (anim)', () => chart.pan({x: -100}, undefined, 'default')); +createButton('Zoom x: 0..-100, y: 0..100', () => { + chart.zoomScale('x', {min: -100, max: 0}, 'default'); + chart.zoomScale('y', {min: 0, max: 100}, 'default'); +}); +createButton('Reset zoom', () => chart.resetZoom()); + +function createButton(label, handler) { + const btn = document.createElement('button'); + btn.textContent = label; + btn.addEventListener('click', handler); + document.body.append(btn); +} diff --git a/docs/samples/api.md b/docs/samples/api.md index 7e67a67c..7f90381b 100644 --- a/docs/samples/api.md +++ b/docs/samples/api.md @@ -1,115 +1,11 @@ # API -```js chart-editor -// -const NUMBER_CFG = {count: 20, min: -100, max: 100} -const data = { - datasets: [{ - label: 'My First dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }, { - label: 'My Second dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }] -} -// +
-// -const scaleOpts = { - reverse: true, - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } -} -const scales = { - x: { - position: 'top', - }, - y: { - position: 'right', - }, -} -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) -// + diff --git a/docs/samples/basic.js b/docs/samples/basic.js new file mode 100644 index 00000000..faad72e3 --- /dev/null +++ b/docs/samples/basic.js @@ -0,0 +1,128 @@ +/* playground-hide */ +import Chart from '../scripts/register.js'; +import * as Utils from '../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const data = { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }, { + label: 'My Second dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scaleOpts = { + reverse: true, + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } +}; +const scales = { + x: { + position: 'top', + }, + y: { + position: 'right', + }, +}; +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +/* playground-fold-end */ + +// zoom +const zoomOptions = { + limits: { + x: {min: -200, max: 200, minRange: 50}, + y: {min: -200, max: 200, minRange: 50} + }, + pan: { + enabled: true, + mode: 'xy', + }, + zoom: { + wheel: { + enabled: true, + }, + pinch: { + enabled: true + }, + mode: 'xy', + onZoomComplete({chart}) { + // This update is needed to display up to date zoom level in the title. + // Without this, previous zoom level is displayed. + // The reason is: title uses the same beforeUpdate hook, and is evaluated before zoom. + chart.update('none'); + } + } +}; + +// chart +/* playground-fold */ +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; +const zoomStatus = (chart) => (zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled') + ' (' + chart.getZoomLevel() + 'x)'; + +const ctx = document.querySelector('canvas') +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: zoomOptions, + title: { + display: true, + position: 'bottom', + text: (ctx) => 'Zoom: ' + zoomStatus(ctx.chart) + ', Pan: ' + panStatus() + } + }, + onClick(e) { + console.log(e.type); + } + } +}); +/* playground-fold-end */ + +// buttons +/* playground-fold */ +createButton('Toggle zoom', () => { + zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled; + zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled; + chart.update(); +}); +createButton('Toggle pan', () => { + zoomOptions.pan.enabled = !zoomOptions.pan.enabled; + chart.update(); +}); +createButton('Reset zoom', () => chart.resetZoom()); + +function createButton(label, handler) { + const btn = document.createElement('button'); + btn.textContent = label; + btn.addEventListener('click', handler); + document.body.append(btn); + } +/* playground-fold-end */ diff --git a/docs/samples/basic.md b/docs/samples/basic.md index 03f54d84..40b24c09 100644 --- a/docs/samples/basic.md +++ b/docs/samples/basic.md @@ -1,132 +1,11 @@ # Basic -```js chart-editor -// -const NUMBER_CFG = {count: 20, min: -100, max: 100} -const data = { - datasets: [{ - label: 'My First dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }, { - label: 'My Second dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }] -} -// +
-// -const scaleOpts = { - reverse: true, - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } -} -const scales = { - x: { - position: 'top', - }, - y: { - position: 'right', - }, -} -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) -// + diff --git a/docs/samples/drag/category.js b/docs/samples/drag/category.js new file mode 100644 index 00000000..a517b147 --- /dev/null +++ b/docs/samples/drag/category.js @@ -0,0 +1,88 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const DATA_COUNT = 20; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [{ + label: 'Dataset 1', + borderColor: Utils.randomColor(0.7), + backgroundColor: Utils.randomColor(0.5), + data: Utils.numbers(NUMBER_CFG), + }, { + label: 'Dataset 2', + borderColor: Utils.randomColor(0.7), + backgroundColor: Utils.randomColor(0.5), + data: Utils.numbers(NUMBER_CFG), + }, { + label: 'Dataset 3', + borderColor: Utils.randomColor(0.7), + backgroundColor: Utils.randomColor(0.5), + data: Utils.numbers(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scaleOpts = { + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } +}; +const scales = { + x: { + type: 'category', + }, + y: { + type: 'linear', + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + }, +}; +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +/* playground-fold-end */ + +// chart +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'bar', + data: data, + options: { + scales: scales, + plugins: { + tooltip: false, + zoom: { + pan: { + enabled: true, + mode: 'x', + modifierKey: 'ctrl', + }, + zoom: { + drag: { + enabled: true + }, + mode: 'x', + }, + } + }, + } +}); + +// button +/* playground-fold */ +const btn = document.createElement('button'); +btn.textContent = 'Reset zoom'; +btn.addEventListener('click', () => chart.resetZoom()); +document.body.append(btn); +/* playground-fold-end */ diff --git a/docs/samples/drag/category.md b/docs/samples/drag/category.md index 14c51c02..899c893b 100644 --- a/docs/samples/drag/category.md +++ b/docs/samples/drag/category.md @@ -2,93 +2,12 @@ Zooming is performed by clicking and selecting an area over the chart with the mouse. Pan is activated by keeping `ctrl` pressed. -```js chart-editor -// -const DATA_COUNT = 20 -const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100} -const data = { - labels: Utils.months({count: DATA_COUNT}), - datasets: [{ - label: 'Dataset 1', - borderColor: Utils.randomColor(0.7), - backgroundColor: Utils.randomColor(0.5), - data: Utils.numbers(NUMBER_CFG), - }, { - label: 'Dataset 2', - borderColor: Utils.randomColor(0.7), - backgroundColor: Utils.randomColor(0.5), - data: Utils.numbers(NUMBER_CFG), - }, { - label: 'Dataset 3', - borderColor: Utils.randomColor(0.7), - backgroundColor: Utils.randomColor(0.5), - data: Utils.numbers(NUMBER_CFG), - }] -} -// +
-// -const scaleOpts = { - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } -} -const scales = { - x: { - type: 'category', - }, - y: { - type: 'linear', - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - }, -} -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) -// + diff --git a/docs/samples/drag/linear.js b/docs/samples/drag/linear.js new file mode 100644 index 00000000..765866a0 --- /dev/null +++ b/docs/samples/drag/linear.js @@ -0,0 +1,111 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const data = { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }, { + label: 'My Second dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scaleOpts = { + reverse: true, + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } +}; +const scales = { + x: { + position: 'top', + }, + y: { + position: 'right', + }, +}; +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +/* playground-fold-end */ + +// zoom +const dragColor = Utils.randomColor(0.4); +const zoomOptions = { + pan: { + enabled: true, + mode: 'xy', + modifierKey: 'ctrl', + }, + zoom: { + mode: 'xy', + drag: { + enabled: true, + borderColor: 'rgb(54, 162, 235)', + borderWidth: 1, + backgroundColor: 'rgba(54, 162, 235, 0.3)' + } + } +}; + +// chart +/* playground-fold */ +const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled'; + +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: zoomOptions, + title: { + display: true, + position: 'bottom', + text: (ctx) => 'Zoom: ' + zoomStatus() + } + }, + } +}); +/* playground-fold-end */ + +// buttons +/* playground-fold */ +createButton('Toggle zoom', () => { + zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled; + chart.update(); +}); +createButton('Reset zoom', () => chart.resetZoom()); + +function createButton(label, handler) { + const btn = document.createElement('button'); + btn.textContent = label; + btn.addEventListener('click', handler); + document.body.append(btn); +} +/* playground-fold-end */ diff --git a/docs/samples/drag/linear.md b/docs/samples/drag/linear.md index 2668430a..9415056b 100644 --- a/docs/samples/drag/linear.md +++ b/docs/samples/drag/linear.md @@ -2,112 +2,12 @@ Zooming is performed by clicking and selecting an area over the chart with the mouse. Pan is activated by keeping `ctrl` pressed. -```js chart-editor -// -const NUMBER_CFG = {count: 20, min: -100, max: 100} -const data = { - datasets: [{ - label: 'My First dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }, { - label: 'My Second dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }] -} -// +
-// -const scaleOpts = { - reverse: true, - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } -} -const scales = { - x: { - position: 'top', - }, - y: { - position: 'right', - }, -} -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) -// + diff --git a/docs/samples/drag/log.js b/docs/samples/drag/log.js new file mode 100644 index 00000000..4381d9e3 --- /dev/null +++ b/docs/samples/drag/log.js @@ -0,0 +1,167 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const data = { + datasets: [{ + label: 'V(node2)', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: [{ + x: 1, + y: -1.711e-2, + }, { + x: 1.26, + y: -2.708e-2, + }, { + x: 1.58, + y: -4.285e-2, + }, { + x: 2.0, + y: -6.772e-2, + }, { + x: 2.51, + y: -1.068e-1, + }, { + x: 3.16, + y: -1.681e-1, + }, { + x: 3.98, + y: -2.635e-1, + }, { + x: 5.01, + y: -4.106e-1, + }, { + x: 6.31, + y: -6.339e-1, + }, { + x: 7.94, + y: -9.659e-1, + }, { + x: 10.00, + y: -1.445, + }, { + x: 12.6, + y: -2.110, + }, { + x: 15.8, + y: -2.992, + }, { + x: 20.0, + y: -4.102, + }, { + x: 25.1, + y: -5.429, + }, { + x: 31.6, + y: -6.944, + }, { + x: 39.8, + y: -8.607, + }, { + x: 50.1, + y: -1.038e1, + }, { + x: 63.1, + y: -1.223e1, + }, { + x: 79.4, + y: -1.413e1, + }, { + x: 100.00, + y: -1.607e1, + }, { + x: 126, + y: -1.803e1, + }, { + x: 158, + y: -2e1, + }, { + x: 200, + y: -2.199e1, + }, { + x: 251, + y: -2.398e1, + }, { + x: 316, + y: -2.597e1, + }, { + x: 398, + y: -2.797e1, + }, { + x: 501, + y: -2.996e1, + }, { + x: 631, + y: -3.196e1, + }, { + x: 794, + y: -3.396e1, + }, { + x: 1000, + y: -3.596e1 + }] + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scales = { + x: { + type: 'logarithmic', + ticks: { + callback: function(tick) { + const remain = tick / (Math.pow(10, Math.floor(Math.log10(tick)))); + if (remain === 1 || remain === 2 || remain === 5) { + return tick.toString() + 'Hz'; + } + return ''; + }, + maxRotation: 0 + }, + title: { + display: true, + text: 'Frequency', + }, + } +}; +/* playground-fold-end */ + +// chart +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: { + pan: { + enabled: true, + mode: 'xy', + modifierKey: 'ctrl', + }, + zoom: { + drag: { + enabled: true + }, + mode: 'xy', + }, + } + }, + } +}); + +// button +/* playground-fold */ +const btn = document.createElement('button'); +btn.textContent = 'Reset zoom'; +btn.addEventListener('click', () => chart.resetZoom()); +document.body.appendChild(btn); +/* playground-fold-end */ diff --git a/docs/samples/drag/log.md b/docs/samples/drag/log.md index db26d52f..e32f5cb7 100644 --- a/docs/samples/drag/log.md +++ b/docs/samples/drag/log.md @@ -2,172 +2,12 @@ Zooming is performed by clicking and selecting an area over the chart with the mouse. Pan is activated by keeping `ctrl` pressed. -```js chart-editor -// -const data = { - datasets: [{ - label: 'V(node2)', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: [{ - x: 1, - y: -1.711e-2, - }, { - x: 1.26, - y: -2.708e-2, - }, { - x: 1.58, - y: -4.285e-2, - }, { - x: 2.0, - y: -6.772e-2, - }, { - x: 2.51, - y: -1.068e-1, - }, { - x: 3.16, - y: -1.681e-1, - }, { - x: 3.98, - y: -2.635e-1, - }, { - x: 5.01, - y: -4.106e-1, - }, { - x: 6.31, - y: -6.339e-1, - }, { - x: 7.94, - y: -9.659e-1, - }, { - x: 10.00, - y: -1.445, - }, { - x: 12.6, - y: -2.110, - }, { - x: 15.8, - y: -2.992, - }, { - x: 20.0, - y: -4.102, - }, { - x: 25.1, - y: -5.429, - }, { - x: 31.6, - y: -6.944, - }, { - x: 39.8, - y: -8.607, - }, { - x: 50.1, - y: -1.038e1, - }, { - x: 63.1, - y: -1.223e1, - }, { - x: 79.4, - y: -1.413e1, - }, { - x: 100.00, - y: -1.607e1, - }, { - x: 126, - y: -1.803e1, - }, { - x: 158, - y: -2e1, - }, { - x: 200, - y: -2.199e1, - }, { - x: 251, - y: -2.398e1, - }, { - x: 316, - y: -2.597e1, - }, { - x: 398, - y: -2.797e1, - }, { - x: 501, - y: -2.996e1, - }, { - x: 631, - y: -3.196e1, - }, { - x: 794, - y: -3.396e1, - }, { - x: 1000, - y: -3.596e1 - }] - }] -} -// +
-// -const scales = { - x: { - type: 'logarithmic', - ticks: { - callback: function(tick) { - const remain = tick / (Math.pow(10, Math.floor(Math.log10(tick)))) - if (remain === 1 || remain === 2 || remain === 5) { - return tick.toString() + 'Hz' - } - return '' - }, - maxRotation: 0 - }, - title: { - display: true, - text: 'Frequency', - }, - } -} -// + diff --git a/docs/samples/drag/time.js b/docs/samples/drag/time.js new file mode 100644 index 00000000..84631837 --- /dev/null +++ b/docs/samples/drag/time.js @@ -0,0 +1,122 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const NUMBER_CFG = {count: 500, min: 0, max: 1000}; +const data = { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.hourlyPoints(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scales = { + x: { + position: 'bottom', + type: 'time', + ticks: { + autoSkip: true, + autoSkipPadding: 50, + maxRotation: 0 + }, + time: { + displayFormats: { + hour: 'HH:mm', + minute: 'HH:mm', + second: 'HH:mm:ss' + } + } + }, + y: { + position: 'right', + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } + }, +}; +/* playground-fold-end */ + +// zoom +const zoomOptions = { + pan: { + enabled: true, + modifierKey: 'ctrl', + }, + zoom: { + drag: { + enabled: true + }, + mode: 'xy', + }, +}; + +// chart +/* playground-fold */ +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; +const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled'; + +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: zoomOptions, + title: { + display: true, + position: 'bottom', + text: (ctx) => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() + } + }, + } +}); +/* playground-fold-end */ + +// buttons +/* playground-fold */ +createButton('Toggle zoom', () => { + zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled; + chart.update(); +}); +createButton('Toggle pan', () => { + zoomOptions.pan.enabled = !zoomOptions.pan.enabled; + chart.update(); +}); +createButton('Reset zoom', () => { + chart.resetZoom(); +}); +createButton('Zoom to next week', () => { + chart.zoomScale('x', Utils.nextWeek(), 'default'); + chart.update(); +}); +createButton('Zoom to 400-600', () => { + chart.zoomScale('y', {min: 400, max: 600}, 'default'); + chart.update(); +}); + +function createButton(label, handler) { + const btn = document.createElement('button'); + btn.textContent = label; + btn.addEventListener('click', handler); + document.body.append(btn); +} +/* playground-fold-end */ diff --git a/docs/samples/drag/time.md b/docs/samples/drag/time.md index 417c9ca4..ae6fe45c 100644 --- a/docs/samples/drag/time.md +++ b/docs/samples/drag/time.md @@ -2,129 +2,12 @@ Zooming is performed by clicking and selecting an area over the chart with the mouse. Pan is activated by keeping `ctrl` pressed. -```js chart-editor -// -const NUMBER_CFG = {count: 500, min: 0, max: 1000} -const data = { - datasets: [{ - label: 'My First dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.hourlyPoints(NUMBER_CFG), - }] -} -// +
-// -const scales = { - x: { - position: 'bottom', - type: 'time', - ticks: { - autoSkip: true, - autoSkipPadding: 50, - maxRotation: 0 - }, - time: { - displayFormats: { - hour: 'HH:mm', - minute: 'HH:mm', - second: 'HH:mm:ss' - } - } - }, - y: { - position: 'right', - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } - }, -} -// + diff --git a/docs/samples/drag/timeseries.js b/docs/samples/drag/timeseries.js new file mode 100644 index 00000000..b7442fbb --- /dev/null +++ b/docs/samples/drag/timeseries.js @@ -0,0 +1,116 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const NUMBER_CFG = {count: 200, min: 0, max: 100}; +const data = { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.officeHourPoints(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scales = { + x: { + position: 'bottom', + type: 'timeseries', + ticks: { + autoSkip: true, + autoSkipPadding: 50, + maxRotation: 0, + major: { + enabled: true + } + }, + time: { + displayFormats: { + hour: 'HH:mm', + minute: 'HH:mm', + second: 'HH:mm:ss' + } + } + }, + y: { + position: 'right', + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } + }, +}; +/* playground-fold-end */ + +// zoom +const zoomOptions = { + pan: { + enabled: true, + modifierKey: 'ctrl', + }, + zoom: { + drag: { + enabled: true, + }, + mode: 'xy', + }, +}; + +// chart +/* playground-fold */ +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; +const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled'; + +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: zoomOptions, + title: { + display: true, + position: 'bottom', + text: (ctx) => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() + } + }, + } +}); +/* playground-fold-end */ + +// buttons +/* playground-fold */ +createButton('Toggle zoom', () => { + zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled; + chart.update(); +}); +createButton('Toggle pan', () => { + zoomOptions.pan.enabled = !zoomOptions.pan.enabled; + chart.update(); +}); +createButton('Reset zoom', () => chart.resetZoom()); + +function createButton(label, handler) { + const btn = document.createElement('button'); + btn.textContent = label; + btn.addEventListener('click', handler); + document.body.append(btn); +} +/* playground-fold-end */ + diff --git a/docs/samples/drag/timeseries.md b/docs/samples/drag/timeseries.md index d8d9141e..edf571e5 100644 --- a/docs/samples/drag/timeseries.md +++ b/docs/samples/drag/timeseries.md @@ -2,120 +2,12 @@ Zooming is performed by clicking and selecting an area over the chart with the mouse. Pan is activated by keeping `ctrl` pressed. -```js chart-editor -// -const NUMBER_CFG = {count: 200, min: 0, max: 100} -const data = { - datasets: [{ - label: 'My First dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.officeHourPoints(NUMBER_CFG), - }] -} -// +
-// -const scales = { - x: { - position: 'bottom', - type: 'timeseries', - ticks: { - autoSkip: true, - autoSkipPadding: 50, - maxRotation: 0, - major: { - enabled: true - } - }, - time: { - displayFormats: { - hour: 'HH:mm', - minute: 'HH:mm', - second: 'HH:mm:ss' - } - } - }, - y: { - position: 'right', - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } - }, -} -// + diff --git a/docs/samples/fetch-data.js b/docs/samples/fetch-data.js new file mode 100644 index 00000000..80560715 --- /dev/null +++ b/docs/samples/fetch-data.js @@ -0,0 +1,144 @@ +/* playground-hide */ +import Chart from '../scripts/register.js'; +import * as Utils from '../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const start = new Date().valueOf(); +const end = start + 1000 * 60 * 60 * 24 * 2; +const allData = []; +let y = 100; +for (let x = start; x <= end; x += 1000) { + y += 5 - Math.random() * 10; + allData.push({x, y}); +} + +function fetchData(x1, x2) { + const step = Math.max(1, Math.round((x2 - x1) / 100000)); + const data = []; + let i = 0; + while (i < allData.length && allData[i].x < x1) { + i++; + } + while (i < allData.length && allData[i].x <= x2) { + data.push(allData[i]); + i += step; + } + return data; +} +/* playground-fold-end */ + +// fetch +/* playground-fold */ +let timer; +function startFetch({chart}) { + const {min, max} = chart.scales.x; + clearTimeout(timer); + timer = setTimeout(() => { + console.log('Fetched data between ' + min + ' and ' + max); + chart.data.datasets[0].data = fetchData(min, max); + chart.stop(); // make sure animations are not running + chart.update('none'); + }, 500); +} +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scales = { + x: { + position: 'bottom', + min: start, + max: end, + type: 'time', + ticks: { + autoSkip: true, + autoSkipPadding: 50, + maxRotation: 0 + }, + time: { + displayFormats: { + hour: 'HH:mm', + minute: 'HH:mm', + second: 'HH:mm:ss' + } + } + }, + y: { + type: 'linear', + position: 'left', + }, +}; +/* playground-fold-end */ + +// zoom +const zoomOptions = { + limits: { + x: {min: 'original', max: 'original', minRange: 60 * 1000}, + }, + pan: { + enabled: true, + mode: 'x', + modifierKey: 'ctrl', + onPanComplete: startFetch + }, + zoom: { + wheel: { + enabled: true, + }, + drag: { + enabled: true, + }, + pinch: { + enabled: true + }, + mode: 'x', + onZoomComplete: startFetch + } +}; +const zoomStatus = (chart) => 'zoom level: ' + chart.getZoomLevel() + ''; + +// chart +/* playground-fold */ +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'line', + data: { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: fetchData(start, end), + }] + }, + options: { + scales: scales, + plugins: { + zoom: zoomOptions, + title: { + display: true, + position: 'bottom', + text: (ctx) => zoomStatus(ctx.chart) + } + }, + transitions: { + zoom: { + animation: { + duration: 100 + } + } + } + } +}); +/* playground-fold-end */ + +// button +/* playground-fold */ +const resetZoomBtn = document.createElement('button'); +resetZoomBtn.textContent = 'Reset zoom'; +document.body.append(resetZoomBtn); +resetZoomBtn.addEventListener('click', () => chart.resetZoom()); +/* playground-fold-end */ diff --git a/docs/samples/fetch-data.md b/docs/samples/fetch-data.md index 8bd7396d..1cc08def 100644 --- a/docs/samples/fetch-data.md +++ b/docs/samples/fetch-data.md @@ -1,148 +1,11 @@ # Fetch Data -```js chart-editor -// -const start = new Date().valueOf() -const end = start + 1000 * 60 * 60 * 24 * 2 -const allData = [] -let y = 100 -for (let x = start; x <= end; x += 1000) { - y += 5 - Math.random() * 10 - allData.push({x, y}) -} +
-function fetchData(x1, x2) { - const step = Math.max(1, Math.round((x2 - x1) / 100000)) - const data = [] - let i = 0 - while (i < allData.length && allData[i].x < x1) { - i++ - } - while (i < allData.length && allData[i].x <= x2) { - data.push(allData[i]) - i += step - } - return data -} -// + diff --git a/docs/samples/pan-region.js b/docs/samples/pan-region.js new file mode 100644 index 00000000..3b1fca49 --- /dev/null +++ b/docs/samples/pan-region.js @@ -0,0 +1,113 @@ +/* playground-hide */ +import Chart from '../scripts/register.js'; +import * as Utils from '../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const data = { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }, { + label: 'My Second dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scaleOpts = { + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } +}; +const scales = { + x: { + position: 'top', + }, + y: { + position: 'right', + }, +}; +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +/* playground-fold-end */ + +// zoom +const zoomOptions = { + limits: { + x: {min: -200, max: 200, minRange: 50}, + y: {min: -200, max: 200, minRange: 50} + }, + pan: { + enabled: true, + onPanStart({chart, point}) { + const area = chart.chartArea; + const w25 = area.width * 0.25; + const h25 = area.height * 0.25; + if (point.x < area.left + w25 || point.x > area.right - w25 + || point.y < area.top + h25 || point.y > area.bottom - h25) { + return false; // abort + } + }, + mode: 'xy', + }, + zoom: { + wheel: { + enabled: false, + }, + pinch: { + enabled: true + }, + } +}; + +// border +/* playground-fold */ +const borderPlugin = { + id: 'panAreaBorder', + beforeDraw(chart, args, options) { + const {ctx, chartArea: {left, top, width, height}} = chart; + ctx.save(); + ctx.strokeStyle = 'rgba(255, 0, 0, 0.3)'; + ctx.lineWidth = 1; + ctx.strokeRect(left + width * 0.25, top + height * 0.25, width / 2, height / 2); + ctx.restore(); + } +}; +/* playground-fold-end */ + +// chart +/* playground-fold */ +const ctx = document.querySelector('canvas'); +new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: zoomOptions, + }, + }, + plugins: [borderPlugin] +}); +/* playground-fold-end */ diff --git a/docs/samples/pan/region.md b/docs/samples/pan/region.md index 73596596..2edb33c5 100644 --- a/docs/samples/pan/region.md +++ b/docs/samples/pan/region.md @@ -2,114 +2,12 @@ In this example pan is only accepted at the middle region (50%) of the chart. This region is highlighted by a red border. -```js chart-editor -// -const NUMBER_CFG = {count: 20, min: -100, max: 100} -const data = { - datasets: [{ - label: 'My First dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }, { - label: 'My Second dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }] -} -// +
-// -const scaleOpts = { - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } -} -const scales = { - x: { - position: 'top', - }, - y: { - position: 'right', - }, -} -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) -// + diff --git a/docs/samples/wheel/category.js b/docs/samples/wheel/category.js new file mode 100644 index 00000000..314946ff --- /dev/null +++ b/docs/samples/wheel/category.js @@ -0,0 +1,88 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const DATA_COUNT = 20; +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const data = { + labels: Utils.months({count: DATA_COUNT}), + datasets: [{ + label: 'Dataset 1', + borderColor: Utils.randomColor(0.7), + backgroundColor: Utils.randomColor(0.5), + data: Utils.numbers(NUMBER_CFG), + }, { + label: 'Dataset 2', + borderColor: Utils.randomColor(0.7), + backgroundColor: Utils.randomColor(0.5), + data: Utils.numbers(NUMBER_CFG), + }, { + label: 'Dataset 3', + borderColor: Utils.randomColor(0.7), + backgroundColor: Utils.randomColor(0.5), + data: Utils.numbers(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scaleOpts = { + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } +}; +const scales = { + x: { + type: 'category', + min: 5, + max: 11, + }, + y: { + type: 'linear' + }, +}; +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +/* playground-fold-end */ + +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'bar', + data: data, + options: { + scales: scales, + plugins: { + zoom: { + pan: { + enabled: true, + mode: 'xy', + threshold: 5, + }, + zoom: { + wheel: { + enabled: true + }, + pinch: { + enabled: true + }, + mode: 'xy', + }, + } + }, + } +}); + +// button +/* playground-fold */ +const btn = document.createElement('button'); +btn.textContent = 'Reset zoom'; +btn.addEventListener('click', () => chart.resetZoom()); +document.body.append(btn); +/* playground-fold-end */ diff --git a/docs/samples/wheel/category.md b/docs/samples/wheel/category.md index 4c86d289..2730f454 100644 --- a/docs/samples/wheel/category.md +++ b/docs/samples/wheel/category.md @@ -1,93 +1,11 @@ # Category Scale -```js chart-editor -// -const DATA_COUNT = 20 -const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100} -const data = { - labels: Utils.months({count: DATA_COUNT}), - datasets: [{ - label: 'Dataset 1', - borderColor: Utils.randomColor(0.7), - backgroundColor: Utils.randomColor(0.5), - data: Utils.numbers(NUMBER_CFG), - }, { - label: 'Dataset 2', - borderColor: Utils.randomColor(0.7), - backgroundColor: Utils.randomColor(0.5), - data: Utils.numbers(NUMBER_CFG), - }, { - label: 'Dataset 3', - borderColor: Utils.randomColor(0.7), - backgroundColor: Utils.randomColor(0.5), - data: Utils.numbers(NUMBER_CFG), - }] -} -// +
-// -const scaleOpts = { - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } -} -const scales = { - x: { - type: 'category', - min: 5, - max: 11, - }, - y: { - type: 'linear' - }, -} -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) -// + diff --git a/docs/samples/wheel/click-zoom.js b/docs/samples/wheel/click-zoom.js new file mode 100644 index 00000000..1e71e15b --- /dev/null +++ b/docs/samples/wheel/click-zoom.js @@ -0,0 +1,125 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const DATA_COUNT = 70; +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; + +const labels = Utils.months({count: DATA_COUNT}); +const data = { + labels: labels, + datasets: [ + { + label: 'Dataset 1', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + stack: 'combined', + type: 'bar' + }, + { + label: 'Dataset 2', + data: Utils.numbers(NUMBER_CFG), + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + stack: 'combined' + } + ] +}; +/* playground-fold-end */ + +// zoom +const zoomOptions = { + limits: { + y: {min: 0, max: 200, minRange: 50} + }, + pan: { + enabled: true, + mode: 'xy', + }, + zoom: { + wheel: { + enabled: false, + }, + pinch: { + enabled: false + }, + mode: 'xy', + } +}; + +// border +/* playground-fold */ +const borderPlugin = { + id: 'chartAreaBorder', + beforeDraw(chart, args, options) { + const {ctx, chartArea: {left, top, width, height}} = chart; + if (chart.options.plugins.zoom.zoom.wheel.enabled) { + ctx.save(); + ctx.strokeStyle = 'red'; + ctx.lineWidth = 1; + ctx.strokeRect(left, top, width, height); + ctx.restore(); + } + } +}; + +const zoomStatus = () => 'Zoom: ' + (zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled'); +/* playground-fold-end */ + +// chart +/* playground-fold */ +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'line', + data: data, + options: { + scales: {y: {stacked: true, min: 0}}, + plugins: { + zoom: zoomOptions, + title: { + display: true, + position: 'bottom', + text: zoomStatus + } + }, + onClick(e) { + const chart = e.chart; + chart.options.plugins.zoom.zoom.wheel.enabled = !chart.options.plugins.zoom.zoom.wheel.enabled; + chart.options.plugins.zoom.zoom.pinch.enabled = !chart.options.plugins.zoom.zoom.pinch.enabled; + chart.update(); + } + }, + plugins: [borderPlugin] +}); +/* playground-fold-end */ + +// buttons +/* playground-fold */ +createButton('Randomize', () => { + chart.data.datasets.forEach(dataset => { + dataset.data = Utils.numbers(NUMBER_CFG); + }); + chart.update(); +}); +createButton('Toggle zoom', () => { + zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled; + chart.update(); +}); +createButton('Toggle pan', () => { + zoomOptions.pan.enabled = !zoomOptions.pan.enabled; + chart.update(); +}); +createButton('Reset zoom', () => chart.resetZoom()); + +function createButton(label, handler) { + const btn = document.createElement('button'); + btn.textContent = label; + btn.addEventListener('click', handler); + document.body.append(btn); +} +/* playground-fold-end */ \ No newline at end of file diff --git a/docs/samples/wheel/click-zoom.md b/docs/samples/wheel/click-zoom.md index d329f605..e8f56bc6 100644 --- a/docs/samples/wheel/click-zoom.md +++ b/docs/samples/wheel/click-zoom.md @@ -17,134 +17,7 @@ Ut tortor pretium viverra suspendisse potenti nullam ac tortor. Mauris a diam ma Tortor condimentum lacinia quis vel eros donec ac. Phasellus vestibulum lorem sed risus ultricies tristique. Vitae tortor condimentum lacinia quis vel eros donec. Morbi tempus iaculis urna id volutpat lacus laoreet non curabitur. Ut pharetra sit amet aliquam id diam. Eu non diam phasellus vestibulum lorem. Pharetra pharetra massa massa ultricies mi. Donec ultrices tincidunt arcu non. Sagittis orci a scelerisque purus semper eget duis. In iaculis nunc sed augue lacus viverra. Vitae proin sagittis nisl rhoncus mattis rhoncus urna neque. Consequat mauris nunc congue nisi vitae suscipit tellus mauris a. Massa placerat duis ultricies lacus sed turpis tincidunt id. Sit amet tellus cras adipiscing enim eu turpis. Amet porttitor eget dolor morbi non arcu risus quis varius. Potenti nullam ac tortor vitae purus. -```js chart-editor -// -const DATA_COUNT = 70 -const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100} - -const labels = Utils.months({count: DATA_COUNT}) -const data = { - labels: labels, - datasets: [ - { - label: 'Dataset 1', - data: Utils.numbers(NUMBER_CFG), - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - stack: 'combined', - type: 'bar' - }, - { - label: 'Dataset 2', - data: Utils.numbers(NUMBER_CFG), - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - stack: 'combined' - } - ] -} -// - -// -const zoomOptions = { - limits: { - y: {min: 0, max: 200, minRange: 50} - }, - pan: { - enabled: true, - mode: 'xy', - }, - zoom: { - wheel: { - enabled: false, - }, - pinch: { - enabled: false - }, - mode: 'xy', - } -} -// - -// -const borderPlugin = { - id: 'chartAreaBorder', - beforeDraw(chart) { - const {ctx, chartArea: {left, top, width, height}} = chart - if (chart.options.plugins.zoom.zoom.wheel.enabled) { - ctx.save() - ctx.strokeStyle = 'red' - ctx.lineWidth = 1 - ctx.strokeRect(left, top, width, height) - ctx.restore() - } - } -} -// - -const zoomStatus = () => 'Zoom: ' + (zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled') - -// -const config = { - type: 'line', - data: data, - options: { - scales: {y: {stacked: true, min: 0}}, - plugins: { - zoom: zoomOptions, - title: { - display: true, - position: 'bottom', - text: zoomStatus - } - }, - onClick(e) { - const chart = e.chart - chart.options.plugins.zoom.zoom.wheel.enabled = !chart.options.plugins.zoom.zoom.wheel.enabled - chart.options.plugins.zoom.zoom.pinch.enabled = !chart.options.plugins.zoom.zoom.pinch.enabled - chart.update() - } - }, - plugins: [borderPlugin] -} -// - -const actions = [ - { - name: 'Randomize', - handler(chart) { - chart.data.datasets.forEach(dataset => { - dataset.data = Utils.numbers(NUMBER_CFG) - }) - chart.update() - } - }, { - name: 'Toggle zoom', - handler(chart) { - zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled - chart.update() - } - }, { - name: 'Toggle pan', - handler(chart) { - zoomOptions.pan.enabled = !zoomOptions.pan.enabled - chart.update() - }, - }, { - name: 'Reset zoom', - handler(chart) { - chart.resetZoom() - } - } -] - -module.exports = { - actions, - config, -} -``` +
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Fringilla ut morbi tincidunt augue interdum velit euismod. Elit pellentesque habitant morbi tristique senectus et netus. Consectetur adipiscing elit pellentesque habitant morbi. Id faucibus nisl tincidunt eget nullam non nisi est sit. Blandit turpis cursus in hac habitasse. Vulputate eu scelerisque felis imperdiet proin fermentum leo vel. Ornare massa eget egestas purus. A diam sollicitudin tempor id eu nisl nunc. Augue mauris augue neque gravida in fermentum et sollicitudin. Dolor purus non enim praesent elementum facilisis leo vel fringilla. Habitant morbi tristique senectus et netus et malesuada. Nulla pharetra diam sit amet nisl suscipit adipiscing bibendum est. Gravida dictum fusce ut placerat orci nulla pellentesque. @@ -155,3 +28,11 @@ Volutpat lacus laoreet non curabitur. Diam donec adipiscing tristique risus. Fus Ut tortor pretium viverra suspendisse potenti nullam ac tortor. Mauris a diam maecenas sed enim. Tellus in hac habitasse platea dictumst vestibulum rhoncus. Cras sed felis eget velit aliquet. Purus viverra accumsan in nisl nisi. Sed risus ultricies tristique nulla aliquet enim tortor at. Integer quis auctor elit sed vulputate mi sit amet mauris. Adipiscing elit duis tristique sollicitudin nibh sit amet commodo. Risus feugiat in ante metus dictum at. Posuere urna nec tincidunt praesent semper. Auctor elit sed vulputate mi sit amet mauris commodo. Senectus et netus et malesuada fames ac turpis egestas integer. Varius morbi enim nunc faucibus a pellentesque. Sed felis eget velit aliquet sagittis id. Ac auctor augue mauris augue neque gravida. Etiam erat velit scelerisque in dictum non consectetur a erat. Tortor condimentum lacinia quis vel eros donec ac. Phasellus vestibulum lorem sed risus ultricies tristique. Vitae tortor condimentum lacinia quis vel eros donec. Morbi tempus iaculis urna id volutpat lacus laoreet non curabitur. Ut pharetra sit amet aliquam id diam. Eu non diam phasellus vestibulum lorem. Pharetra pharetra massa massa ultricies mi. Donec ultrices tincidunt arcu non. Sagittis orci a scelerisque purus semper eget duis. In iaculis nunc sed augue lacus viverra. Vitae proin sagittis nisl rhoncus mattis rhoncus urna neque. Consequat mauris nunc congue nisi vitae suscipit tellus mauris a. Massa placerat duis ultricies lacus sed turpis tincidunt id. Sit amet tellus cras adipiscing enim eu turpis. Amet porttitor eget dolor morbi non arcu risus quis varius. Potenti nullam ac tortor vitae purus. + + \ No newline at end of file diff --git a/docs/samples/wheel/log.js b/docs/samples/wheel/log.js new file mode 100644 index 00000000..d4ddab11 --- /dev/null +++ b/docs/samples/wheel/log.js @@ -0,0 +1,173 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const data = { + datasets: [{ + label: 'V(node2)', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: [{ + x: 1, + y: -1.711e-2, + }, { + x: 1.26, + y: -2.708e-2, + }, { + x: 1.58, + y: -4.285e-2, + }, { + x: 2.0, + y: -6.772e-2, + }, { + x: 2.51, + y: -1.068e-1, + }, { + x: 3.16, + y: -1.681e-1, + }, { + x: 3.98, + y: -2.635e-1, + }, { + x: 5.01, + y: -4.106e-1, + }, { + x: 6.31, + y: -6.339e-1, + }, { + x: 7.94, + y: -9.659e-1, + }, { + x: 10.00, + y: -1.445, + }, { + x: 12.6, + y: -2.110, + }, { + x: 15.8, + y: -2.992, + }, { + x: 20.0, + y: -4.102, + }, { + x: 25.1, + y: -5.429, + }, { + x: 31.6, + y: -6.944, + }, { + x: 39.8, + y: -8.607, + }, { + x: 50.1, + y: -1.038e1, + }, { + x: 63.1, + y: -1.223e1, + }, { + x: 79.4, + y: -1.413e1, + }, { + x: 100.00, + y: -1.607e1, + }, { + x: 126, + y: -1.803e1, + }, { + x: 158, + y: -2e1, + }, { + x: 200, + y: -2.199e1, + }, { + x: 251, + y: -2.398e1, + }, { + x: 316, + y: -2.597e1, + }, { + x: 398, + y: -2.797e1, + }, { + x: 501, + y: -2.996e1, + }, { + x: 631, + y: -3.196e1, + }, { + x: 794, + y: -3.396e1, + }, { + x: 1000, + y: -3.596e1 + }] + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scales = { + x: { + type: 'logarithmic', + ticks: { + callback: function(tick) { + const remain = tick / (Math.pow(10, Math.floor(Math.log10(tick)))); + if (remain === 1 || remain === 2 || remain === 5) { + return tick.toString() + 'Hz'; + } + return ''; + }, + maxRotation: 0 + }, + title: { + display: true, + text: 'Frequency', + }, + } +}; +/* playground-fold-end */ + +// chart +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: { + limits: { + x: {min: 0.5, max: 2e3, minRange: 100}, + y: {min: -50, max: 10, minRange: 10} + }, + pan: { + enabled: true, + mode: 'xy', + }, + zoom: { + wheel: { + enabled: true + }, + pinch: { + enabled: true, + }, + mode: 'xy', + }, + } + }, + } +}); + +// button +/* playground-fold */ +const btn = document.createElement('button'); +btn.textContent = 'Reset zoom'; +btn.addEventListener('click', () => chart.resetZoom()); +document.body.append(btn); +/* playground-fold-end */ diff --git a/docs/samples/wheel/log.md b/docs/samples/wheel/log.md index 17a0a27b..2bcb0dd8 100644 --- a/docs/samples/wheel/log.md +++ b/docs/samples/wheel/log.md @@ -1,183 +1,11 @@ # Logarithmic Scale -```js chart-editor -// -const data = { - datasets: [{ - label: 'V(node2)', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: [{ - x: 1, - y: -1.711e-2, - }, { - x: 1.26, - y: -2.708e-2, - }, { - x: 1.58, - y: -4.285e-2, - }, { - x: 2.0, - y: -6.772e-2, - }, { - x: 2.51, - y: -1.068e-1, - }, { - x: 3.16, - y: -1.681e-1, - }, { - x: 3.98, - y: -2.635e-1, - }, { - x: 5.01, - y: -4.106e-1, - }, { - x: 6.31, - y: -6.339e-1, - }, { - x: 7.94, - y: -9.659e-1, - }, { - x: 10.00, - y: -1.445, - }, { - x: 12.6, - y: -2.110, - }, { - x: 15.8, - y: -2.992, - }, { - x: 20.0, - y: -4.102, - }, { - x: 25.1, - y: -5.429, - }, { - x: 31.6, - y: -6.944, - }, { - x: 39.8, - y: -8.607, - }, { - x: 50.1, - y: -1.038e1, - }, { - x: 63.1, - y: -1.223e1, - }, { - x: 79.4, - y: -1.413e1, - }, { - x: 100.00, - y: -1.607e1, - }, { - x: 126, - y: -1.803e1, - }, { - x: 158, - y: -2e1, - }, { - x: 200, - y: -2.199e1, - }, { - x: 251, - y: -2.398e1, - }, { - x: 316, - y: -2.597e1, - }, { - x: 398, - y: -2.797e1, - }, { - x: 501, - y: -2.996e1, - }, { - x: 631, - y: -3.196e1, - }, { - x: 794, - y: -3.396e1, - }, { - x: 1000, - y: -3.596e1 - }] - }] -} -// +
-// -const scales = { - x: { - type: 'logarithmic', - ticks: { - callback: function(tick) { - const remain = tick / (Math.pow(10, Math.floor(Math.log10(tick)))) - if (remain === 1 || remain === 2 || remain === 5) { - return tick.toString() + 'Hz' - } - return '' - }, - maxRotation: 0 - }, - title: { - display: true, - text: 'Frequency', - }, - }, - y: { - // constant width for the scale - afterFit: (scale) => { - scale.width = 50 - }, - } -} -// + diff --git a/docs/samples/wheel/over-scale-mode.js b/docs/samples/wheel/over-scale-mode.js new file mode 100644 index 00000000..a5297bf2 --- /dev/null +++ b/docs/samples/wheel/over-scale-mode.js @@ -0,0 +1,117 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const data = { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }, { + label: 'My Second dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.points(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scaleOpts = { + reverse: true, + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } +}; +const scales = { + x: { + position: 'top', + }, + y: { + position: 'right', + }, +}; +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +/* playground-fold-end */ + +// zoom +const zoomOptions = { + zoom: { + wheel: { + enabled: true, + }, + pinch: { + enabled: true, + }, + mode: 'xy', + scaleMode: 'xy', + }, + pan: { + enabled: true, + mode: 'xy', + scaleMode: 'xy', + } +}; + +// chart +/* playground-fold */ +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; +const zoomStatus = () => zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled'; + +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: zoomOptions, + title: { + display: true, + position: 'bottom', + text: (ctx) => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus(), + } + }, + } +}); +/* playground-fold-end */ + +// buttons +/* playground-fold */ +createButton('Toggle zoom', () => { + zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled; + zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled; + chart.update(); +}); +createButton('Toggle pan', () => { + zoomOptions.pan.enabled = !zoomOptions.pan.enabled; + chart.update(); +}); +createButton('Reset zoom', () => chart.resetZoom()); + +function createButton(label, handler) { + const btn = document.createElement('button'); + btn.textContent = label; + btn.addEventListener('click', handler); + document.body.append(btn); +} +/* playground-fold-end */ \ No newline at end of file diff --git a/docs/samples/wheel/over-scale-mode.md b/docs/samples/wheel/over-scale-mode.md index 70e17a95..0036ead1 100644 --- a/docs/samples/wheel/over-scale-mode.md +++ b/docs/samples/wheel/over-scale-mode.md @@ -2,121 +2,12 @@ Pan and Zoom are allowed only when mouse is over the axis. -```js chart-editor -// -const NUMBER_CFG = {count: 20, min: -100, max: 100} -const data = { - datasets: [{ - label: 'My First dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }, { - label: 'My Second dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.points(NUMBER_CFG), - }] -} -// +
-// -const scaleOpts = { - reverse: true, - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } -} -const scales = { - x: { - position: 'top', - }, - y: { - position: 'right', - }, -} -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) -// + diff --git a/docs/samples/wheel/time.js b/docs/samples/wheel/time.js new file mode 100644 index 00000000..82c9b4be --- /dev/null +++ b/docs/samples/wheel/time.js @@ -0,0 +1,127 @@ +/* playground-hide */ +import Chart from '../../scripts/register.js'; +import * as Utils from '../../scripts/utils.js'; +/* playground-hide-end */ +// data +/* playground-fold */ +const NUMBER_CFG = {count: 500, min: 0, max: 1000}; +const data = { + datasets: [{ + label: 'My First dataset', + borderColor: Utils.randomColor(0.4), + backgroundColor: Utils.randomColor(0.1), + pointBorderColor: Utils.randomColor(0.7), + pointBackgroundColor: Utils.randomColor(0.5), + pointBorderWidth: 1, + data: Utils.hourlyPoints(NUMBER_CFG), + }] +}; +/* playground-fold-end */ + +// scales +/* playground-fold */ +const scales = { + x: { + position: 'bottom', + type: 'time', + ticks: { + autoSkip: true, + autoSkipPadding: 50, + maxRotation: 0 + }, + time: { + displayFormats: { + hour: 'HH:mm', + minute: 'HH:mm', + second: 'HH:mm:ss' + } + } + }, + y: { + position: 'right', + ticks: { + callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, + }, + grid: { + borderColor: Utils.randomColor(1), + color: 'rgba( 0, 0, 0, 0.1)', + }, + title: { + display: true, + text: (ctx) => ctx.scale.axis + ' axis', + } + }, +}; +/* playground-fold-end */ + +// zoom +const zoomOptions = { + zoom: { + wheel: { + enabled: true, + }, + pinch: { + enabled: true, + }, + mode: 'xy', + }, + pan: { + enabled: true, + mode: 'xy', + } +}; + +// chart +/* playground-fold */ +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; +const zoomStatus = () => zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled'; + +const ctx = document.querySelector('canvas'); +const chart = new Chart(ctx, { + type: 'scatter', + data: data, + options: { + scales: scales, + plugins: { + zoom: zoomOptions, + title: { + display: true, + position: 'bottom', + text: (ctx) => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() + } + }, + onClick(e) { + console.log(e.type); + } + } +}); +/* playground-fold-end */ + +// buttons +/* playground-fold */ +createButton('Toggle zoom', () => { + zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled; + zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled; + chart.update(); +}); +createButton('Toggle pan', () => { + zoomOptions.pan.enabled = !zoomOptions.pan.enabled; + chart.update(); +}); +createButton('Reset zoom', () => chart.resetZoom()); +createButton('Zoom to next week', () => { + chart.zoomScale('x', Utils.nextWeek(), 'default'); + chart.update(); +}); +createButton('Zoom to 400-600', () => { + chart.zoomScale('y', {min: 400, max: 600}, 'default'); + chart.update(); +}); + +function createButton(label, handler) { + const btn = document.createElement('button'); + btn.textContent = label; + btn.addEventListener('click', handler); + document.body.append(btn); + } +/* playground-fold-end */ diff --git a/docs/samples/wheel/time.md b/docs/samples/wheel/time.md index b99dc068..2ae84fd7 100644 --- a/docs/samples/wheel/time.md +++ b/docs/samples/wheel/time.md @@ -1,136 +1,11 @@ # Time Scale -```js chart-editor -// -const NUMBER_CFG = {count: 500, min: 0, max: 1000} -const data = { - datasets: [{ - label: 'My First dataset', - borderColor: Utils.randomColor(0.4), - backgroundColor: Utils.randomColor(0.1), - pointBorderColor: Utils.randomColor(0.7), - pointBackgroundColor: Utils.randomColor(0.5), - pointBorderWidth: 1, - data: Utils.hourlyPoints(NUMBER_CFG), - }] -} -// +
-// -const scales = { - x: { - position: 'bottom', - type: 'time', - ticks: { - autoSkip: true, - autoSkipPadding: 50, - maxRotation: 0 - }, - time: { - displayFormats: { - hour: 'HH:mm', - minute: 'HH:mm', - second: 'HH:mm:ss' - } - } - }, - y: { - position: 'right', - ticks: { - callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, - }, - grid: { - borderColor: Utils.randomColor(1), - color: 'rgba( 0, 0, 0, 0.1)', - }, - title: { - display: true, - text: (ctx) => ctx.scale.axis + ' axis', - } - }, -} -// + diff --git a/docs/scripts/defaults.js b/docs/scripts/defaults.js deleted file mode 100644 index 3067f74d..00000000 --- a/docs/scripts/defaults.js +++ /dev/null @@ -1,17 +0,0 @@ -import { defaults } from 'chart.js' - -defaults.set({ - datasets: { - line: { - tension: 0.4, - }, - }, - interaction: { - mode: 'nearest', - axis: 'x', - intersect: false, - }, - plugins: { - legend: false, - }, -}) diff --git a/docs/scripts/register.js b/docs/scripts/register.js index ae09ef23..1086334e 100644 --- a/docs/scripts/register.js +++ b/docs/scripts/register.js @@ -1,23 +1,42 @@ -import Chart from 'chart.js/auto' -import 'chartjs-adapter-date-fns' -import zoomPlugin from '../../dist/chartjs-plugin-zoom.esm.js' +import Chart from 'chart.js/auto'; +import 'chartjs-adapter-date-fns'; +import zoomPlugin from '../../dist/chartjs-plugin-zoom.esm.js'; +import {defaults} from 'chart.js'; Chart.register(zoomPlugin) Chart.register({ id: 'version', afterDraw(chart) { - const ctx = chart.ctx - ctx.save() - ctx.font = '9px monospace' - ctx.fillStyle = 'rgba(0, 0, 0, 0.5)' - ctx.textAlign = 'right' - ctx.textBaseline = 'top' + const ctx = chart.ctx; + ctx.save(); + ctx.font = '9px monospace'; + ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; + ctx.textAlign = 'right'; + ctx.textBaseline = 'top'; ctx.fillText( 'Chart.js v' + Chart.version + ' + chartjs-plugin-zoom v' + zoomPlugin.version, chart.chartArea.right, 0 - ) - ctx.restore() + ); + ctx.restore(); + } +}); + +defaults.set({ + datasets: { + line: { + tension: 0.4 + } + }, + interaction: { + mode: 'nearest', + axis: 'x', + intersect: false }, -}) + plugins: { + legend: false + }, +}); + +export default Chart; diff --git a/docs/scripts/setup-sample.js b/docs/scripts/setup-sample.js new file mode 100644 index 00000000..7890ddc9 --- /dev/null +++ b/docs/scripts/setup-sample.js @@ -0,0 +1,46 @@ +import { rand } from "./utils"; +import registerBundle from "../public/register.bundle.esm.js?raw"; +import utilsBundle from "../public/utils.bundle.esm.js?raw"; +import styles from "../public/sample-styles.css?raw"; + +export async function setupSample(code, {height = '450px'} = {}) { + await import("playground-elements/playground-project.js"); + await import("playground-elements/playground-file-editor.js"); + await import("playground-elements/playground-preview.js"); + const projectElem = document.createElement('playground-project'); + projectElem.id = `project-${rand(-10000000, 1000000)}`; + const previewElem = document.createElement('playground-preview'); + previewElem.project = projectElem.id; + previewElem.style.height = height; + const editorElem = document.createElement('playground-file-editor'); + editorElem.project = projectElem.id; + projectElem.config = { + files: { + 'index.js': { + content: code.replaceAll('../../scripts/', './') + .replaceAll('../scripts/', './'), + }, + 'index.html': { + content: ` + + + + + +