Skip to content

Scatter

Scatter renders finite data points as SVG circles. Fill, opacity, and outline props can be scalar values or arrays aligned with the input point list. Enable interactive when points should emit pointer events.

Import

ts
import { Scatter } from '@putianyi888/vue3-plots'

Example

vue
<Scatter
  :points="points"
  :domain="domain"
  :size="{ width: 640, height: 360 }"
  :radius="4"
  :fill-color="['#2563eb', '#dc2626']"
  interactive
  @point-click="handlePointClick"
/>

Props

PropTypeDefaultDescription
domainPlotDomainData-space bounds used to map points onto the plot area.
sizePlotSizeOuter SVG size in pixels.
paddingPlotPaddingInsets shared with other plot layers.
strokeOpacityMaybeArray<number>1Circle outline opacity.
strokeWidthMaybeArray<number>1Circle outline width in pixels.
strokeColorMaybeArray<string>'none'Circle outline color.
pointsPlotPoint<T>[]RequiredData points to render.
radiusMaybeArray<number>0Circle radius in pixels.
fillOpacityMaybeArray<number>1Circle fill opacity.
interactivebooleanWhether points emit pointer events and use interactive pointer styling.
fillColorMaybeArray<string>'black'Circle fill color.

Events

EventPayloadDescription
pointClickPlotPoint<T>Emitted when a rendered point is clicked.
pointEnterPlotPoint<T>Emitted when the pointer enters a rendered point.
pointLeavePlotPoint<T>Emitted when the pointer leaves a rendered point.

Slots

SlotPropsDescription
point{ index: number; x: number; y: number; point: PlotPoint<T>; }Custom renderer for a data point marker.

This slot is useful for customizing marker shapes, but it can also render more complex SVG. The example below uses it to mimic line chart and bar chart.

0123423456
vue
<template>
    <div class="example-frame" :style="{ height: `${size.height}px`, width: `${size.width}px` }">
        <TransformGroup ref="transformGroup" :domain="domain" :size="size" :padding="padding">
            <Grid :x-ticks="xTicks" :y-ticks="yTicks" stroke-color="#e2e8f0" />
            <Scatter :points="points">
                <template #point="{ index, x, y, point }">
                    <rect
                        :x="-barWidth / 2" :y="0"
                        :width="barWidth"
                        :height="transformGroup?.scaleY(domain.yMin) - transformGroup?.scaleY(point.y)"
                        fill="#2563eb" fill-opacity="0.78"
                    />
                    <line
                        v-if="index > 0"
                        :x1="transformGroup?.scaleX(points[index - 1].x) - x"
                        :y1="transformGroup?.scaleY(points[index - 1].y) - y"
                        x2="0" y2="0" stroke="#2563eb" stroke-width="2"
                        vector-effect="non-scaling-stroke"
                    />
                    <circle cx="0" cy="0" r="4" fill="#0f172a" />
                </template>
            </Scatter>
            <Axis direction="horizontal" :ticks="xTicks" />
            <Axis direction="vertical" :ticks="yTicks" />
        </TransformGroup>
    </div>
</template>

<script setup lang="ts">
import { computed, useTemplateRef } from 'vue'
import { Axis, Grid, Scatter, TransformGroup, getDataDomain, getNiceTicks } from '@putianyi888/vue3-plots'
import type { PlotPadding, PlotPoint, PlotSize } from '@putianyi888/vue3-plots'

const points: PlotPoint[] = [
    { x: 0, y: 2 },
    { x: 1, y: 4 },
    { x: 2, y: 3 },
    { x: 3, y: 6 },
    { x: 4, y: 5 },
]

const size: PlotSize = { width: 640, height: 360 }
const padding: PlotPadding = { top: 20, right: 20, bottom: 40, left: 40 }
const domain = getDataDomain(points, 0.1)
const xTicks = getNiceTicks(domain.xMin, domain.xMax)
const yTicks = getNiceTicks(domain.yMin, domain.yMax)
const transformGroup = useTemplateRef('transformGroup')

const barWidth = computed(() => Math.abs(transformGroup.value?.scaleX(domain.xMin + 0.64) - transformGroup.value?.scaleX(domain.xMin)))
</script>

<style scoped>
.example-frame {
  overflow-x: auto;
  padding: 16px;
  position: relative;
}
</style>

Notes

Array-valued style props use the original input point index, even when earlier points are skipped because their coordinates are not finite.