Skip to content

Commit

Permalink
feat(axes): use same code for svg & canvas ticks
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Aug 31, 2017
1 parent 18768d0 commit c8c693a
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 132 deletions.
97 changes: 24 additions & 73 deletions src/components/axes/Axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,9 @@ import compose from 'recompose/compose'
import pure from 'recompose/pure'
import { TransitionMotion, spring } from 'react-motion'
import { withMotion } from '../../hocs'
import { computeAxisTicks } from '../../lib/axes'
import AxisTick from './AxisTick'

const center = scale => {
let offset = scale.bandwidth() / 2
if (scale.round()) {
offset = Math.round(offset)
}

return d => scale(d) + offset
}

const axisPositions = ['top', 'right', 'bottom', 'left']
const legendPositions = ['start', 'center', 'end']

Expand Down Expand Up @@ -80,55 +72,15 @@ const Axis = ({
motionStiffness,
motionDamping,
}) => {
let values
if (scale.ticks) {
values = scale.ticks()
} else {
values = scale.domain()
}

const orient = _orient || _position
const position = scale.bandwidth ? center(scale) : scale

let x = 0
let y = 0
let translate
let textAnchor
let textDY
const tickLine = { x2: 0, y2: 0 }
const textXY = { x: 0, y: 0 }

if (['top', 'bottom'].includes(orient)) {
translate = d => ({ x: position(d), y: 0 })

textAnchor = 'middle'
textDY = orient === 'top' ? '0em' : '0.71em'
if ((orient === 'bottom' && tickRotation < 0) || (orient === 'top' && tickRotation > 0)) {
textAnchor = 'end'
textDY = '0.32em'
} else if (
(orient === 'bottom' && tickRotation > 0) ||
(orient === 'top' && tickRotation < 0)
) {
textAnchor = 'start'
textDY = '0.32em'
}
textXY.y = (tickSize + tickPadding) * (orient === 'bottom' ? 1 : -1)

tickLine.y2 = tickSize * (orient === 'bottom' ? 1 : -1)

if (orient === 'bottom') y = height
} else if (['left', 'right'].includes(orient)) {
translate = d => ({ x: 0, y: position(d) })

textAnchor = orient === 'left' ? 'end' : 'start'
textDY = '0.32em'
textXY.x = (tickSize + tickPadding) * (orient === 'right' ? 1 : -1)

tickLine.x2 = tickSize * (orient === 'right' ? 1 : -1)

if (orient === 'right') x = width
}
const { x, y, ticks, textAlign, textBaseline } = computeAxisTicks({
width,
height,
scale,
position: _position,
tickSize,
tickPadding,
tickRotation,
})

let legend = null
if (_legend !== undefined) {
Expand Down Expand Up @@ -174,11 +126,6 @@ const Axis = ({
)
}

const ticks = values.map(v => ({
key: v,
...translate(v),
}))

let tickElements
if (!animate) {
tickElements = (
Expand All @@ -188,11 +135,13 @@ const Axis = ({
key={tick.key}
value={tick.key}
format={format}
tickLine={tickLine}
lineX={tick.lineX}
lineY={tick.lineY}
rotate={tickRotation}
textXY={textXY}
textDY={textDY}
textAnchor={textAnchor}
textX={tick.textX}
textY={tick.textY}
textBaseline={textBaseline}
textAnchor={textAlign}
theme={theme}
x={tick.x}
y={tick.y}
Expand All @@ -213,7 +162,7 @@ const Axis = ({
styles={ticks.map(tick => {
return {
key: `${tick.key}`,
data: tick.key,
data: tick,
style: {
opacity: spring(1, springConfig),
x: spring(tick.x, springConfig),
Expand All @@ -224,16 +173,18 @@ const Axis = ({
>
{interpolatedStyles =>
<g>
{interpolatedStyles.map(({ key, style }) =>
{interpolatedStyles.map(({ key, style, data: tick }) =>
<AxisTick
key={key}
value={key}
format={format}
tickLine={tickLine}
lineX={tick.lineX}
lineY={tick.lineY}
rotate={tickRotation}
textXY={textXY}
textDY={textDY}
textAnchor={textAnchor}
textX={tick.textX}
textY={tick.textY}
textBaseline={textBaseline}
textAnchor={textAlign}
theme={theme}
{...style}
/>
Expand Down
14 changes: 8 additions & 6 deletions src/components/axes/AxisTick.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ export default class AxisTick extends Component {
opacity,
rotate,
format,
tickLine,
textXY,
textDY,
lineX,
lineY,
textX,
textY,
textBaseline,
textAnchor,
theme,
} = this.props
Expand All @@ -46,11 +48,11 @@ export default class AxisTick extends Component {

return (
<g transform={`translate(${x},${y})`} style={{ opacity }}>
<line {...tickLine} stroke={theme.axis.tickColor} />
<line x1={0} x2={lineX} y1={0} y2={lineY} stroke={theme.axis.tickColor} />
<text
dy={textDY}
alignmentBaseline={textBaseline}
textAnchor={textAnchor}
transform={`translate(${textXY.x},${textXY.y}) rotate(${rotate})`}
transform={`translate(${textX},${textY}) rotate(${rotate})`}
style={{
fill: theme.axis.textColor,
fontSize: theme.axis.fontSize,
Expand Down
71 changes: 50 additions & 21 deletions src/lib/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,33 @@ const centerScale = scale => {
return d => scale(d) + offset
}

const textPropsByEngine = {
svg: {
align: {
left: 'start',
center: 'middle',
right: 'end',
},
baseline: {
top: 'before-edge',
center: 'central',
bottom: 'after-edge',
},
},
canvas: {
align: {
left: 'left',
center: 'center',
right: 'right',
},
baseline: {
top: 'top',
center: 'middle',
bottom: 'bottom',
},
},
}

/**
* @typedef {Object} AxisTick
* @param {number} x
Expand All @@ -41,6 +68,7 @@ const centerScale = scale => {
* @param {number} [tickSize=5]
* @param {number} [tickPadding=5]
* @param {number} [tickRotation=0]
* @parem {string} [engine='svg']
* @return {{ x: number, y: number, ticks: Array.<AxisTick>, textAlign: string, textBaseline: string }}
*/
export const computeAxisTicks = ({
Expand All @@ -54,6 +82,8 @@ export const computeAxisTicks = ({
tickPadding = 5,
tickRotation = 0,
//format,

engine = 'svg',
}) => {
let values
if (scale.ticks) {
Expand All @@ -62,6 +92,8 @@ export const computeAxisTicks = ({
values = scale.domain()
}

const textProps = textPropsByEngine[engine]

const orient = _position
const position = scale.bandwidth ? centerScale(scale) : scale
const line = { lineX: 0, lineY: 0 }
Expand All @@ -70,8 +102,8 @@ export const computeAxisTicks = ({
let x = 0
let y = 0
let translate
let textAlign = 'center'
let textBaseline = 'middle'
let textAlign = textProps.align.center
let textBaseline = textProps.baseline.center

if (horizontalPositions.includes(orient)) {
translate = d => ({ x: position(d), y: 0 })
Expand All @@ -81,25 +113,25 @@ export const computeAxisTicks = ({

if (orient === 'bottom') {
y = height
textBaseline = 'top'
textBaseline = textProps.baseline.top
} else {
textBaseline = 'bottom'
textBaseline = textProps.baseline.bottom
}

if (tickRotation === 0) {
textAlign = 'center'
textAlign = textProps.align.center
} else if (
(orient === 'bottom' && tickRotation < 0) ||
(orient === 'top' && tickRotation > 0)
) {
textAlign = 'right'
textBaseline = 'middle'
textAlign = textProps.align.right
textBaseline = textProps.baseline.center
} else if (
(orient === 'bottom' && tickRotation > 0) ||
(orient === 'top' && tickRotation < 0)
) {
textAlign = 'left'
textBaseline = 'middle'
textAlign = textProps.align.left
textBaseline = textProps.baseline.center
}
} else if (verticalPositions.includes(orient)) {
translate = d => ({ x: 0, y: position(d) })
Expand All @@ -109,22 +141,19 @@ export const computeAxisTicks = ({

if (orient === 'right') {
x = width
textAlign = 'left'
textAlign = textProps.align.left
} else {
textAlign = 'right'
textAlign = textProps.align.right
}
}

const ticks = values.map(value => {
const position = translate(value)

return {
value,
...position,
...line,
...text,
}
})
const ticks = values.map(value => ({
key: value,
value,
...translate(value),
...line,
...text,
}))

return {
x,
Expand Down
1 change: 1 addition & 0 deletions src/lib/canvas/axes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const renderAxis = (
tickSize,
tickPadding,
tickRotation,
engine: 'canvas',
})

ctx.save()
Expand Down
Loading

0 comments on commit c8c693a

Please sign in to comment.