Columns in Vue
Using ApexCharts with Vue
This Columns example wires ApexCharts into Vue through the vue3-apexcharts component.
Install it with npm install vue3-apexcharts apexcharts, then mount the chart with <apexchart type="..." :options="options" :series="series" />.
<template>
<div>
<div class="wrap">
<h1>Highlight a part of every column</h1>
<p>
Pick a sales channel. Every column stays on the chart at its full value,
faded, and the part that channel sold is drawn solid in front of it from
the same baseline. Labels and tooltips state the part.
</p>
<div class="actions">
<span>Channel</span>
<button data-channel="online" class="active">Online</button>
<button data-channel="store">In store</button>
<button data-channel="">All (clear)</button>
</div>
<div class="chart-wrap">
<div id="chart">
<apexchart
type="bar"
height="380"
:options="chartOptions"
:series="series"
></apexchart>
</div>
</div>
</div>
</div>
</template>
<script>
import VueApexCharts from 'vue-apexcharts'
import ApexCharts from 'apexcharts'
import 'apexcharts/features/highlight-filter'
// Shared by the vanilla-js, React and Vue builds.
//
// Units sold per year by region (the wholes), and what each sales channel
// contributed to them (the parts). Picking a channel keeps every year and
// region on the chart: each column is drawn faded at its full value, with the
// channel's share solid in front.
var YEARS = ['2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022']
var REGIONS = ['Americas', 'Europe', 'Asia Pacific']
var UNITS = [
[34, 43, 66, 69, 58, 40, 78, 80],
[21, 26, 29, 32, 47, 46, 50, 96],
[19, 24, 28, 26, 38, 31, 52, 38]
]
var CHANNELS = {
online: [
[26, 29, 50, 48, 37, 26, 55, 40],
[14, 17, 21, 20, 23, 34, 38, 70],
[12, 18, 22, 20, 26, 21, 38, 19]
],
store: [
[8, 14, 16, 21, 21, 14, 23, 40],
[7, 9, 8, 12, 24, 12, 12, 26],
[7, 6, 6, 6, 12, 10, 14, 19]
]
}
function seriesFor(channel) {
return REGIONS.map(function (name, i) {
var s = { name: name, data: UNITS[i] }
if (channel) s.highlightData = CHANNELS[channel][i]
return s
})
}
// One call per pick: the parts go in, the chart animates from what is on
// screen. Clearing is the same call without them.
function pickChannel(chart, channel) {
document.querySelectorAll('[data-channel]').forEach(function (b) {
b.classList.toggle('active', b.getAttribute('data-channel') === channel)
})
return chart.updateSeries(seriesFor(channel))
}
function wireChannels(chart) {
document.querySelectorAll('[data-channel]').forEach(function (b) {
b.addEventListener('click', function () {
pickChannel(chart, b.getAttribute('data-channel'))
})
})
}
export default {
components: {
apexchart: VueApexCharts,
},
data: function () {
return {
series: seriesFor('online'),
chartOptions: {
chart: {
id: 'units',
type: 'bar',
height: 380,
toolbar: { show: false },
},
colors: ['#16a34a', '#f59e0b', '#2563eb'],
plotOptions: {
bar: { columnWidth: '78%' },
},
dataLabels: { enabled: false },
stroke: { show: true, width: 1, colors: ['transparent'] },
xaxis: { categories: YEARS },
yaxis: { title: { text: 'Units (thousands)' } },
highlightFilter: { fadeOpacity: 0.2 },
legend: { position: 'bottom' },
tooltip: { shared: true, intersect: false },
},
channelTimer: null,
}
},
mounted: function () {
// The vue-apexcharts wrapper owns the render, so reach the live instance by
// its chart.id. (seriesFor / pickChannel live in the shared head script.)
var me = this
me.channelTimer = window.setInterval(function () {
var chart = ApexCharts.getChartByID('units')
if (!chart) return
window.clearInterval(me.channelTimer)
wireChannels(chart)
}, 50)
},
beforeDestroy: function () {
window.clearInterval(this.channelTimer)
},,
}
</script>
<style>
body {
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #fafbfc;
color: #2c3e50;
margin: 0;
padding: 32px 16px;
}
.wrap {
max-width: 720px;
margin: 0 auto;
}
h1 {
font-size: 22px;
margin: 0 0 8px;
}
p {
color: #5b6b78;
line-height: 1.5;
margin: 0 0 20px;
}
.chart-wrap {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
}
.actions {
margin: 0 0 14px;
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.actions span {
font-size: 13px;
color: #5b6b78;
margin-right: 4px;
}
.actions button {
color: #2c3e50;
background: #fff;
border: 1px solid #cfd8e3;
padding: 6px 14px;
font-weight: 600;
font-size: 13px;
border-radius: 6px;
cursor: pointer;
}
.actions button.active {
color: #fff;
background: #2563eb;
border-color: #2563eb;
}
</style>