# # 3.x Migration Guide

Chart.js 3.0 introduces a number of breaking changes. Chart.js 2.0 was released in April 2016. In the years since then, as Chart.js has grown in popularity and feature set, we've learned some lessons about how to better create a charting library. In order to improve performance, offer new features, and improve maintainability, it was necessary to break backwards compatibility, but we aimed to do so only when worth the benefit. Some major highlights of v3 include:

- Large performance improvements including the ability to skip data parsing and render charts in parallel via webworkers
- Additional configurability and scriptable options with better defaults
- Completely rewritten animation system
- Rewritten filler plugin with numerous bug fixes
- Documentation migrated from GitBook to Vuepress
- API documentation generated and verified by TypeDoc
- No more CSS injection
- Tons of bug fixes
- Tree shaking

## # End user migration

### # Setup and installation

- Distributed files are now in lower case. For example: `dist/chart.js`.
- Chart.js is no longer providing the `Chart.bundle.js` and `Chart.bundle.min.js`. Please see the installation and integration docs for details on the recommended way to setup Chart.js if you were using these builds.
- `moment` is no longer specified as an npm dependency. If you are using the `time` or `timeseries` scales, you must include one of the available adapters
  (opens new window) and corresponding date library. You no longer need to exclude moment from your build.
- The `Chart` constructor will throw an error if the canvas/context provided is already in use
- Chart.js 3 is tree-shakeable. So if you are using it as an `npm` module in a project and want to make use of this feature, you need to import and register the controllers, elements, scales and plugins you want to use, for a list of all the available items to import see integration. You will not have to call `register` if importing Chart.js via a `script` tag or from the `auto` register path as an `npm` module, in this case you will not get the tree shaking benefits. Here is an example of registering components:

    import { Chart, LineController, LineElement, PointElement, LinearScale, Title } from `chart.js`
    Chart.register(LineController, LineElement, PointElement, LinearScale, Title);
    const chart = new Chart(ctx, {
        type: 'line',
        // data: ...
        options: {
            plugins: {
                title: {
                    display: true,
                    text: 'Chart Title'
                }
            },
            scales: {
                x: {
                    type: 'linear'
                },
                y: {
                    type: 'linear'
                }
            }
        }
    })

### # Chart types

- `horizontalBar` chart type was removed. Horizontal bar charts can be configured using the new `indexAxis` option

### # Options

A number of changes were made to the configuration options passed to the `Chart` constructor. Those changes are documented below.

#### # Generic changes

- Indexable options are now looping. `backgroundColor: ['red', 'green']` will result in alternating `'red'` / `'green'` if there are more than 2 data points.
- The input properties of object data can now be freely specified, see data structures for details.
- Most options are resolved utilizing proxies, instead of merging with defaults. In addition to easily enabling different resolution routes for different contexts, it allows using other resolved options in scriptable options.
  - Options are by default scriptable and indexable, unless disabled for some reason.
  - Scriptable options receive a option resolver as second parameter for accessing other options in same context.
  - Resolution falls to upper scopes, if no match is found earlier. See options for details.

#### # Specific changes

- `elements.rectangle` is now `elements.bar`
- `hover.animationDuration` is now configured in `animation.active.duration`
- `responsiveAnimationDuration` is now configured in `animation.resize.duration`
- Polar area `elements.arc.angle` is now configured in degrees instead of radians.
- Polar area `startAngle` option is now consistent with `Radar`, 0 is at top and value is in degrees. Default is changed from `-½π` to `0`.
- Doughnut `rotation` option is now in degrees and 0 is at top. Default is changed from `-½π` to `0`.
- Doughnut `circumference` option is now in degrees. Default is changed from `2π` to `360`.
- Doughnut `cutoutPercentage` was renamed to `cutout`and accepts pixels as number and percent as string ending with `%`.
- `scale` option was removed in favor of `options.scales.r` (or any other scale id, with `axis: 'r'`)
- `scales.[x/y]Axes` arrays were removed. Scales are now configured directly to `options.scales` object with the object key being the scale Id.
- `scales.[x/y]Axes.barPercentage` was moved to dataset option `barPercentage`
- `scales.[x/y]Axes.barThickness` was moved to dataset option `barThickness`
- `scales.[x/y]Axes.categoryPercentage` was moved to dataset option `categoryPercentage`
- `scales.[x/y]Axes.maxBarThickness` was moved to dataset option `maxBarThickness`
- `scales.[x/y]Axes.minBarLength` was moved to dataset option `minBarLength`
- `scales.[x/y]Axes.scaleLabel` was renamed to `scales[id].title`
- `scales.[x/y]Axes.scaleLabel.labelString` was renamed to `scales[id].title.text`
- `scales.[x/y]Axes.ticks.beginAtZero` was renamed to `scales[id].beginAtZero`
- `scales.[x/y]Axes.ticks.max` was renamed to `scales[id].max`
- `scales.[x/y]Axes.ticks.min` was renamed to `scales[id].min`
- `scales.[x/y]Axes.ticks.reverse` was renamed to `scales[id].reverse`
- `scales.[x/y]Axes.ticks.suggestedMax` was renamed to `scales[id].suggestedMax`
- `scales.[x/y]Axes.ticks.suggestedMin` was renamed to `scales[id].suggestedMin`
- `scales.[x/y]Axes.ticks.unitStepSize` was removed. Use `scales[id].ticks.stepSize`
- `scales.[x/y]Axes.ticks.userCallback` was renamed to `scales[id].ticks.callback`
- `scales.[x/y]Axes.time.format` was renamed to `scales[id].time.parser`
- `scales.[x/y]Axes.time.max` was renamed to `scales[id].max`
- `scales.[x/y]Axes.time.min` was renamed to `scales[id].min`
- `scales.[x/y]Axes.zeroLine*` options of axes were removed. Use scriptable scale options instead.
- The dataset option `steppedLine` was removed. Use `stepped`
- The chart option `showLines` was renamed to `showLine` to match the dataset option.
- The chart option `startAngle` was moved to `radial` scale options.
- To override the platform class used in a chart instance, pass `platform: PlatformClass` in the config object. Note that the class should be passed, not an instance of the class.
- `aspectRatio` defaults to 1 for doughnut, pie, polarArea, and radar charts
- `TimeScale` does not read `t` from object data by default anymore. The default property is `x` or `y`, depending on the orientation. See data structures for details on how to change the default.
- `tooltips` namespace was renamed to `tooltip` to match the plugin name
- `legend`, `title` and `tooltip` namespaces were moved from `options` to `options.plugins`.
- `tooltips.custom` was renamed to `plugins.tooltip.external`

#### # Defaults

- `global` namespace was removed from `defaults`. So `Chart.defaults.global` is now `Chart.defaults`
- Dataset controller defaults were relocate to `overrides`. For example `Chart.defaults.line` is now `Chart.overrides.line`
- `default` prefix was removed from defaults. For example `Chart.defaults.global.defaultColor` is now `Chart.defaults.color`
- `defaultColor` was split to `color`, `borderColor` and `backgroundColor`
- `defaultFontColor` was renamed to `color`
- `defaultFontFamily` was renamed to `font.family`
- `defaultFontSize` was renamed to `font.size`
- `defaultFontStyle` was renamed to `font.style`
- `defaultLineHeight` was renamed to `font.lineHeight`
- Horizontal Bar default tooltip mode was changed from `'index'` to `'nearest'` to match vertical bar charts
- `legend`, `title` and `tooltip` namespaces were moved from `Chart.defaults` to `Chart.defaults.plugins`.
- `elements.line.fill` default changed from `true` to `false`.
- Line charts no longer override the default `interaction` mode. Default is changed from `'index'` to `'nearest'`.

#### # Scales

The configuration options for scales is the largest change in v3. The `xAxes` and `yAxes` arrays were removed and axis options are individual scales now keyed by scale ID.

The v2 configuration below is shown with it's new v3 configuration

    options: {
      scales: {
        xAxes: [{
          id: 'x',
          type: 'time',
          display: true,
          title: {
            display: true,
            text: 'Date'
          },
          ticks: {
            major: {
              enabled: true
            },
            font: function(context) {
              if (context.tick && context.tick.major) {
                return {
                  weight: 'bold',
                  color: '#FF0000'
                };
              }
            }
          }
        }],
        yAxes: [{
          id: 'y',
          display: true,
          title: {
            display: true,
            text: 'value'
          }
        }]
      }
    }

And now, in v3:

    options: {
      scales: {
        x: {
          type: 'time',
          display: true,
          title: {
            display: true,
            text: 'Date'
          },
          ticks: {
            major: {
              enabled: true
            },
            color: (context) => context.tick && context.tick.major && '#FF0000',
            font: function(context) {
              if (context.tick && context.tick.major) {
                return {
                  weight: 'bold'
                };
              }
            }
          }
        },
        y: {
          display: true,
          title: {
            display: true,
            text: 'value'
          }
        }
      }
    }

- The time scale option `distribution: 'series'` was removed and a new scale type `timeseries` was introduced in its place
- In the time scale, `autoSkip` is now enabled by default for consistency with the other scales

#### # Animations

Animation system was completely rewritten in Chart.js v3. Each property can now be animated separately. Please see animations docs for details.

#### # Customizability

- `custom` attribute of elements was removed. Please use scriptable options
- The `hover` property of scriptable options `context` object was renamed to `active` to align it with the datalabels plugin.

#### # Interactions

- To allow DRY configuration, a root options scope for common interaction options was added. `options.hover` and `options.plugins.tooltip` now both extend from `options.interaction`. Defaults are defined at `defaults.interaction` level, so by default hover and tooltip interactions share the same mode etc.
- `interactions` are now limited to the chart area + allowed overflow
- `{mode: 'label'}` was replaced with `{mode: 'index'}`
- `{mode: 'single'}` was replaced with `{mode: 'nearest', intersect: true}`
- `modes['X-axis']` was replaced with `{mode: 'index', intersect: false}`
- `options.onClick` is now limited to the chart area
- `options.onClick` and `options.onHover` now receive the `chart` instance as a 3rd argument
- `options.onHover` now receives a wrapped `event` as the first parameter. The previous first parameter value is accessible via `event.native`.
- `options.hover.onHover` was removed, use `options.onHover`.

#### # Ticks

- `options.gridLines` was renamed to `options.grid`
- `options.gridLines.offsetGridLines` was renamed to `options.grid.offset`.
- `options.gridLines.tickMarkLength` was renamed to `options.grid.tickLength`.
- `options.ticks.fixedStepSize` is no longer used. Use `options.ticks.stepSize`.
- `options.ticks.major` and `options.ticks.minor` were replaced with scriptable options for tick fonts.
- `Chart.Ticks.formatters.linear` was renamed to `Chart.Ticks.formatters.numeric`.
- `options.ticks.backdropPaddingX` and `options.ticks.backdropPaddingY` were replaced with `options.ticks.backdropPadding` in the radial linear scale.

#### # Tooltip

- `xLabel` and `yLabel` were removed. Please use `label` and `formattedValue`
- The `filter` option will now be passed additional parameters when called and should have the method signature `function(tooltipItem, index, tooltipItems, data)`
- The `custom` callback now takes a context object that has `tooltip` and `chart` properties
- All properties of tooltip model related to the tooltip options have been moved to reside within the `options` property.
- The callbacks no longer are given a `data` parameter. The tooltip item parameter contains the chart and dataset instead
- The tooltip item's `index` parameter was renamed to `dataIndex` and `value` was renamed to `formattedValue`
- The `xPadding` and `yPadding` options were merged into a single `padding` object

## # Developer migration

While the end-user migration for Chart.js 3 is fairly straight-forward, the developer migration can be more complicated. Please reach out for help in the #dev Slack
(opens new window) channel if tips on migrating would be helpful.

Some of the biggest things that have changed:

- There is a completely rewritten and more performant animation system.
  - `Element._model` and `Element._view` are no longer used and properties are now set directly on the elements. You will have to use the method `getProps` to access these properties inside most methods such as `inXRange`/`inYRange` and `getCenterPoint`. Please take a look at the Chart.js-provided elements
    (opens new window) for examples.
  - When building the elements in a controller, it's now suggested to call `updateElement` to provide the element properties. There are also methods such as `getSharedOptions` and `includeOptions` that have been added to skip redundant computation. Please take a look at the Chart.js-provided controllers
    (opens new window) for examples.
- Scales introduced a new parsing API. This API takes user data and converts it into a more standard format. E.g. it allows users to provide numeric data as a `string` and converts it to a `number` where necessary. Previously this was done on the fly as charts were rendered. Now it's done up front with the ability to skip it for better performance if users provide data in the correct format. If you're using standard data format like `x`/`y` you may not need to do anything. If you're using a custom data format you will have to override some of the parse methods in `core.datasetController.js`. An example can be found in chartjs-chart-financial
  (opens new window), which uses an `{o, h, l, c}` data format.

A few changes were made to controllers that are more straight-forward, but will affect all controllers:

- Options:
  - `global` was removed from the defaults namespace as it was unnecessary and sometimes inconsistent
  - Dataset defaults are now under the chart type options instead of vice-versa. This was not able to be done when introduced in 2.x for backwards compatibility. Fixing it removes the biggest stumbling block that new chart developers encountered
  - Scale default options need to be updated as described in the end user migration section (e.g. `x` instead of `xAxes` and `y` instead of `yAxes`)
- `updateElement` was changed to `updateElements` and has a new method signature as described below. This provides performance enhancements such as allowing easier reuse of computations that are common to all elements and reducing the number of function calls

### # Removed

The following properties and methods were removed:

#### # Removed from Chart

- `Chart.animationService`
- `Chart.active`
- `Chart.borderWidth`
- `Chart.chart.chart`
- `Chart.Bar`. New charts are created via `new Chart` and providing the appropriate `type` parameter
- `Chart.Bubble`. New charts are created via `new Chart` and providing the appropriate `type` parameter
- `Chart.Chart`
- `Chart.Controller`
- `Chart.Doughnut`. New charts are created via `new Chart` and providing the appropriate `type` parameter
- `Chart.innerRadius` now lives on doughnut, pie, and polarArea controllers
- `Chart.lastActive`
- `Chart.Legend` was moved to `Chart.plugins.legend._element` and made private
- `Chart.Line`. New charts are created via `new Chart` and providing the appropriate `type` parameter
- `Chart.LinearScaleBase` now must be imported and cannot be accessed off the `Chart` object
- `Chart.offsetX`
- `Chart.offsetY`
- `Chart.outerRadius` now lives on doughnut, pie, and polarArea controllers
- `Chart.plugins` was replaced with `Chart.registry`. Plugin defaults are now in `Chart.defaults.plugins[id]`.
- `Chart.plugins.register` was replaced by `Chart.register`.
- `Chart.PolarArea`. New charts are created via `new Chart` and providing the appropriate `type` parameter
- `Chart.prototype.generateLegend`
- `Chart.platform`. It only contained `disableCSSInjection`. CSS is never injected in v3.
- `Chart.PluginBase`
- `Chart.Radar`. New charts are created via `new Chart` and providing the appropriate `type` parameter
- `Chart.radiusLength`
- `Chart.scaleService` was replaced with `Chart.registry`. Scale defaults are now in `Chart.defaults.scales[type]`.
- `Chart.Scatter`. New charts are created via `new Chart` and providing the appropriate `type` parameter
- `Chart.types`
- `Chart.Title` was moved to `Chart.plugins.title._element` and made private
- `Chart.Tooltip` is now provided by the tooltip plugin. The positioners can be accessed from `tooltipPlugin.positioners`
- `ILayoutItem.minSize`

#### # Removed from Dataset Controllers

- `BarController.getDatasetMeta().bar`
- `DatasetController.addElementAndReset`
- `DatasetController.createMetaData`
- `DatasetController.createMetaDataset`
- `DoughnutController.getRingIndex`

#### # Removed from Elements

- `Element.getArea`
- `Element.height`
- `Element.hidden` was replaced by chart level status, usable with `getDataVisibility(index)` / `toggleDataVisibility(index)`
- `Element.initialize`
- `Element.inLabelRange`
- `Line.calculatePointY`

#### # Removed from Helpers

- `helpers.addEvent`
- `helpers.aliasPixel`
- `helpers.arrayEquals`
- `helpers.configMerge`
- `helpers.findIndex`
- `helpers.findNextWhere`
- `helpers.findPreviousWhere`
- `helpers.extend`. Use `Object.assign` instead
- `helpers.getValueAtIndexOrDefault`. Use `helpers.resolve` instead.
- `helpers.indexOf`
- `helpers.lineTo`
- `helpers.longestText` was made private
- `helpers.max`
- `helpers.measureText` was made private
- `helpers.min`
- `helpers.nextItem`
- `helpers.niceNum`
- `helpers.numberOfLabelLines`
- `helpers.previousItem`
- `helpers.removeEvent`
- `helpers.roundedRect`
- `helpers.scaleMerge`
- `helpers.where`

#### # Removed from Layout

- `Layout.defaults`

#### # Removed from Scales

- `LinearScaleBase.handleDirectionalChanges`
- `LogarithmicScale.minNotZero`
- `Scale.getRightValue`
- `Scale.longestLabelWidth`
- `Scale.longestTextCache` is now private
- `Scale.margins` is now private
- `Scale.mergeTicksOptions`
- `Scale.ticksAsNumbers`
- `Scale.tickValues` is now private
- `TimeScale.getLabelCapacity` is now private
- `TimeScale.tickFormatFunction` is now private

#### # Removed from Plugins (Legend, Title, and Tooltip)

- `IPlugin.afterScaleUpdate`. Use `afterLayout` instead
- `Legend.margins` is now private
- Legend `onClick`, `onHover`, and `onLeave` options now receive the legend as the 3rd argument in addition to implicitly via `this`
- Legend `onClick`, `onHover`, and `onLeave` options now receive a wrapped `event` as the first parameter. The previous first parameter value is accessible via `event.native`.
- `Title.margins` is now private
- The tooltip item's `x` and `y` attributes were replaced by `element`. You can use `element.x` and `element.y` or `element.tooltipPosition()` instead.

#### # Removal of Public APIs

The following public APIs were removed.

- `getElementAtEvent` is replaced with `chart.getElementsAtEventForMode(e, 'nearest', { intersect: true }, false)`
- `getElementsAtEvent` is replaced with `chart.getElementsAtEventForMode(e, 'index', { intersect: true }, false)`
- `getElementsAtXAxis` is replaced with `chart.getElementsAtEventForMode(e, 'index', { intersect: false }, false)`
- `getDatasetAtEvent` is replaced with `chart.getElementsAtEventForMode(e, 'dataset', { intersect: true }, false)`

#### # Removal of private APIs

The following private APIs were removed.

- `Chart._bufferedRender`
- `Chart._updating`
- `Chart.data.datasets[datasetIndex]._meta`
- `DatasetController._getIndexScaleId`
- `DatasetController._getIndexScale`
- `DatasetController._getValueScaleId`
- `DatasetController._getValueScale`
- `Element._ctx`
- `Element._model`
- `Element._view`
- `LogarithmicScale._valueOffset`
- `TimeScale.getPixelForOffset`
- `TimeScale.getLabelWidth`
- `Tooltip._lastActive`

### # Renamed

The following properties were renamed during v3 development:

- `Chart.Animation.animationObject` was renamed to `Chart.Animation`
- `Chart.Animation.chartInstance` was renamed to `Chart.Animation.chart`
- `Chart.canvasHelpers` was merged with `Chart.helpers`
- `Chart.elements.Arc` was renamed to `Chart.elements.ArcElement`
- `Chart.elements.Line` was renamed to `Chart.elements.LineElement`
- `Chart.elements.Point` was renamed to `Chart.elements.PointElement`
- `Chart.elements.Rectangle` was renamed to `Chart.elements.BarElement`
- `Chart.layoutService` was renamed to `Chart.layouts`
- `Chart.pluginService` was renamed to `Chart.plugins`
- `helpers.callCallback` was renamed to `helpers.callback`
- `helpers.drawRoundedRectangle` was renamed to `helpers.roundedRect`
- `helpers.getValueOrDefault` was renamed to `helpers.valueOrDefault`
- `LayoutItem.fullWidth` was renamed to `LayoutItem.fullSize`
- `Point.controlPointPreviousX` was renamed to `Point.cp1x`
- `Point.controlPointPreviousY` was renamed to `Point.cp1y`
- `Point.controlPointNextX` was renamed to `Point.cp2x`
- `Point.controlPointNextY` was renamed to `Point.cp2y`
- `Scale.calculateTickRotation` was renamed to `Scale.calculateLabelRotation`
- `Tooltip.options.legendColorBackgroupd` was renamed to `Tooltip.options.multiKeyBackground`

#### # Renamed private APIs

The private APIs listed below were renamed:

- `BarController.calculateBarIndexPixels` was renamed to `BarController._calculateBarIndexPixels`
- `BarController.calculateBarValuePixels` was renamed to `BarController._calculateBarValuePixels`
- `BarController.getStackCount` was renamed to `BarController._getStackCount`
- `BarController.getStackIndex` was renamed to `BarController._getStackIndex`
- `BarController.getRuler` was renamed to `BarController._getRuler`
- `Chart.destroyDatasetMeta` was renamed to `Chart._destroyDatasetMeta`
- `Chart.drawDataset` was renamed to `Chart._drawDataset`
- `Chart.drawDatasets` was renamed to `Chart._drawDatasets`
- `Chart.eventHandler` was renamed to `Chart._eventHandler`
- `Chart.handleEvent` was renamed to `Chart._handleEvent`
- `Chart.initialize` was renamed to `Chart._initialize`
- `Chart.resetElements` was renamed to `Chart._resetElements`
- `Chart.unbindEvents` was renamed to `Chart._unbindEvents`
- `Chart.updateDataset` was renamed to `Chart._updateDataset`
- `Chart.updateDatasets` was renamed to `Chart._updateDatasets`
- `Chart.updateLayout` was renamed to `Chart._updateLayout`
- `DatasetController.destroy` was renamed to `DatasetController._destroy`
- `DatasetController.insertElements` was renamed to `DatasetController._insertElements`
- `DatasetController.onDataPop` was renamed to `DatasetController._onDataPop`
- `DatasetController.onDataPush` was renamed to `DatasetController._onDataPush`
- `DatasetController.onDataShift` was renamed to `DatasetController._onDataShift`
- `DatasetController.onDataSplice` was renamed to `DatasetController._onDataSplice`
- `DatasetController.onDataUnshift` was renamed to `DatasetController._onDataUnshift`
- `DatasetController.removeElements` was renamed to `DatasetController._removeElements`
- `DatasetController.resyncElements` was renamed to `DatasetController._resyncElements`
- `LayoutItem.isFullWidth` was renamed to `LayoutItem.isFullSize`
- `RadialLinearScale.setReductions` was renamed to `RadialLinearScale._setReductions`
- `RadialLinearScale.pointLabels` was renamed to `RadialLinearScale._pointLabels`
- `Scale.handleMargins` was renamed to `Scale._handleMargins`

### # Changed

The APIs listed in this section have changed in signature or behaviour from version 2.

#### # Changed in Scales

- `Scale.getLabelForIndex` was replaced by `scale.getLabelForValue`
- `Scale.getPixelForValue` now only requires one parameter. For the `TimeScale` that parameter must be millis since the epoch. As a performance optimization, it may take an optional second parameter, giving the index of the data point.

##### # Changed in Ticks

- `Scale.afterBuildTicks` now has no parameters like the other callbacks
- `Scale.buildTicks` is now expected to return tick objects
- `Scale.convertTicksToLabels` was renamed to `generateTickLabels`. It is now expected to set the label property on the ticks given as input
- `Scale.ticks` now contains objects instead of strings
- When the `autoSkip` option is enabled, `Scale.ticks` now contains only the non-skipped ticks instead of all ticks.
- Ticks are now always generated in monotonically increasing order

##### # Changed in Time Scale

- `getValueForPixel` now returns milliseconds since the epoch

#### # Changed in Controllers

##### # Core Controller

- The first parameter to `updateHoverStyle` is now an array of objects containing the `element`, `datasetIndex`, and `index`
- The signature or `resize` changed, the first `silent` parameter was removed.

##### # Dataset Controllers

- `updateElement` was replaced with `updateElements` now taking the elements to update, the `start` index, `count`, and `mode`
- `setHoverStyle` and `removeHoverStyle` now additionally take the `datasetIndex` and `index`

#### # Changed in Interactions

- Interaction mode methods now return an array of objects containing the `element`, `datasetIndex`, and `index`

#### # Changed in Layout

- `ILayoutItem.update` no longer has a return value

#### # Changed in Helpers

All helpers are now exposed in a flat hierarchy, e.g., `Chart.helpers.canvas.clipArea` -> `Chart.helpers.clipArea`

##### # Canvas Helper

- The second parameter to `drawPoint` is now the full options object, so `style`, `rotation`, and `radius` are no longer passed explicitly
- `helpers.getMaximumHeight` was replaced by `helpers.dom.getMaximumSize`
- `helpers.getMaximumWidth` was replaced by `helpers.dom.getMaximumSize`
- `helpers.clear` was renamed to `helpers.clearCanvas` and now takes `canvas` and optionally `ctx` as parameter(s).
- `helpers.retinaScale` accepts optional third parameter `forceStyle`, which forces overriding current canvas style. `forceRatio` no longer falls back to `window.devicePixelRatio`, instead it defaults to `1`.

#### # Changed in Platform

- `Chart.platform` is no longer the platform object used by charts. Every chart instance now has a separate platform instance.
- `Chart.platforms` is an object that contains two usable platform classes, `BasicPlatform` and `DomPlatform`. It also contains `BasePlatform`, a class that all platforms must extend from.
- If the canvas passed in is an instance of `OffscreenCanvas`, the `BasicPlatform` is automatically used.
- `isAttached` method was added to platform.

#### # Changed in IPlugin interface

- All plugin hooks have unified signature with 3 arguments: `chart`, `args` and `options`. This means change in signature for these hooks: `beforeInit`, `afterInit`, `reset`, `beforeLayout`, `afterLayout`, `beforeRender`, `afterRender`, `beforeDraw`, `afterDraw`, `beforeDatasetsDraw`, `afterDatasetsDraw`, `beforeEvent`, `afterEvent`, `resize`, `destroy`.
- `afterDatasetsUpdate`, `afterUpdate`, `beforeDatasetsUpdate`, and `beforeUpdate` now receive `args` object as second argument. `options` argument is always the last and thus was moved from 2nd to 3rd place.
- `afterEvent` and `beforeEvent` now receive a wrapped `event` as the `event` property of the second argument. The native event is available via `args.event.native`.
- Initial `resize` is no longer silent. Meaning that `resize` event can fire between `beforeInit` and `afterInit`
- New hooks: `install`, `start`, `stop`, and `uninstall`
- `afterEvent` should notify about changes that need a render by setting `args.changed` to true. Because the `args` are shared with all plugins, it should only be set to true and not false.