Bubble Chart

options data setup
function channelValue(x, y, values) {return x < 0 && y < 0 ? values[0] : x < 0 ? values[1] : y < 0 ? values[2] : values[3];}function colorize(opaque, context) {var value = context.raw;var x = value.x / 100;var y = value.y / 100;var r = channelValue(x, y, [250, 150, 50, 0]);var g = channelValue(x, y, [0, 50, 150, 250]);var b = channelValue(x, y, [0, 150, 150, 250]);var a = opaque ? 1 : 0.5 * value.v / 1000;return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';}const config = {type: 'bubble',data: data,options: {aspectRatio: 1,plugins: {legend: false,tooltip: false,},elements: {point: {backgroundColor: colorize.bind(null, false),borderColor: colorize.bind(null, true),borderWidth: function(context) {return Math.min(Math.max(1, context.datasetIndex + 1), 8);},hoverBackgroundColor: 'transparent',hoverBorderColor: function(context) {return Utils.color(context.datasetIndex);},hoverBorderWidth: function(context) {return Math.round(8 * context.raw.v / 1000);},radius: function(context) {var size = context.chart.width;var base = Math.abs(context.raw.v) / 1000;return (size / 24) * base;}}}}};
function generateData() {var data = [];var i;for (i = 0; i < DATA_COUNT; ++i) {data.push({x: Utils.rand(MIN_XY, MAX_XY),y: Utils.rand(MIN_XY, MAX_XY),v: Utils.rand(0, 1000)});}return data;}const data = {datasets: [{data: generateData()}, {data: generateData()}]};
const DATA_COUNT = 16;const MIN_XY = -150;const MAX_XY = 100;Utils.srand(110);const actions = [{name: 'Randomize',handler(chart) {chart.data.datasets.forEach(dataset => {dataset.data = generateData();});chart.update();}},];