Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save PBI-DataVizzle/9b21c0f74fbe0b97e71788b17d37e6ba to your computer and use it in GitHub Desktop.
Save PBI-DataVizzle/9b21c0f74fbe0b97e71788b17d37e6ba to your computer and use it in GitHub Desktop.
easing formulas by Madison Giammaria
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"usermeta": {
"version": "01.01",
"developedBy": "Madison Giammaria",
"gitHub": "https://github.com/Giammaria",
"linkedIn": "https://www.linkedin.com/in/madison-giammaria-58463b33",
"email": "[email protected]",
"visualName": "Easing Formulas",
"visualDescription": "Here you can compare different easing functions and see their respective formulas. This isn't a comprehensive list of easing functions, but these are definitely some popular ones. Special thanks to Pavithra Kodmad for writing this excellent article on understanding easing! https://css-tricks.com/ease-y-breezy-a-primer-on-easing-functions/ A few months prior to making this, I would not have thought that this would be feasible in Vega-Lite."
},
"padding": {"top": 0, "right": 10, "bottom": 20, "left": 0},
"title": {
"text": {"signal": "['Easing Formulas']"},
"subtitle": {
"signal": "['Using Timers in Vega-Lite','‎', 't = '+format(t, '0.3f'), 'd = ' + s+'s']"
},
"subtitleFontStyle": "italic",
"subtitleFontSize": 16,
"subtitleFontWeight": 200,
"subtitlePadding": 5,
"fontSize": 20,
"anchor": "start",
"offset": 20
},
"params": [
{"name": "desiredWidth", "init": 1000},
{"name": "desiredHeight", "init": 500},
{
"name": "duration",
"description": "time in seconds to complete one to go from t[0] to t[1]",
"value": 3,
"bind": {
"input": "range",
"min": 1,
"max": 25,
"step": 1,
"name": "Duration (s)"
}
},
{
"name": "colorDomain",
"description": "ease names",
"init": "['Linear','EaseInQuad','EaseOutQuad','EaseInOutQuad','EaseInCubic','EaseOutCubic','EaseInOutCubic']"
},
{
"name": "colorRange",
"description": "ease colors",
"init": "['#003f5c','#ffa600','#374c80','#ff764a','#7a5195','#ef5675','#bc5090']"
},
{
"name": "oscillationChartWidth",
"description": "width of the oscillation chart area",
"init": "desiredWidth*0.45"
},
{
"name": "chartsInnerPadding",
"description": "padding between charts",
"init": "0.1*width"
},
{
"name": "lineChartWidth",
"description": "width of the line chart area",
"update": "desiredWidth-oscillationChartWidth-chartsInnerPadding"
},
{
"name": "toggleOscillationSecondsVisibility",
"description": "show seconds in oscillation chart",
"value": true,
"bind": {"input": "checkbox", "name": "Seconds"}
},
{
"name": "toggleFormulasVisibility",
"description": "show formulas in the oscillation chart",
"value": true,
"bind": {"input": "checkbox", "name": "Formulas"}
},
{
"name": "toggleAllEasingsVisibility",
"description": "show/hide all easings",
"value": false,
"bind": {"input": "checkbox", "name": "Select/Clear All"}
},
{
"name": "showLinear",
"description": "show linear easing",
"value": true,
"bind": {"input": "checkbox", "name": "Linear"},
"on": [
{
"events": {"signal": "toggleAllEasingsVisibility"},
"update": "toggleAllEasingsVisibility"
}
]
},
{
"name": "showEaseInQuad",
"description": "show easeInQuad easing",
"value": false,
"bind": {"input": "checkbox", "name": "EaseInQuad"},
"on": [
{
"events": {"signal": "toggleAllEasingsVisibility"},
"update": "toggleAllEasingsVisibility"
}
]
},
{
"name": "showEaseOutQuad",
"description": "show eaeOutQuad easing",
"value": false,
"bind": {"input": "checkbox", "name": "EaseOutQuad"},
"on": [
{
"events": {"signal": "toggleAllEasingsVisibility"},
"update": "toggleAllEasingsVisibility"
}
]
},
{
"name": "showEaseInOutQuad",
"description": "show easeInOutQuad easing",
"value": true,
"bind": {"input": "checkbox", "name": "EaseInOutQuad"},
"on": [
{
"events": {"signal": "toggleAllEasingsVisibility"},
"update": "toggleAllEasingsVisibility"
}
]
},
{
"name": "showEaseInCubic",
"description": "show easeInCubic easing",
"value": false,
"bind": {"input": "checkbox", "name": "EaseInCubic"},
"on": [
{
"events": {"signal": "toggleAllEasingsVisibility"},
"update": "toggleAllEasingsVisibility"
}
]
},
{
"name": "showEaseOutCubic",
"description": "show easeOutCubic easing",
"value": false,
"bind": {"input": "checkbox", "name": "EaseOutCubic"},
"on": [
{
"events": {"signal": "toggleAllEasingsVisibility"},
"update": "toggleAllEasingsVisibility"
}
]
},
{
"name": "showEaseInOutCubic",
"description": "show easeInOutCubic easing",
"value": false,
"bind": {"input": "checkbox", "name": "EaseInOutCubic"},
"on": [
{
"events": {"signal": "toggleAllEasingsVisibility"},
"update": "toggleAllEasingsVisibility"
}
]
},
{
"name": "xOscillationDomain",
"description": "input range from 0 to duration",
"init": "[0, duration]",
"on": [{"events": {"signal": "duration"}, "update": "[0, duration]"}]
},
{
"name": "timer",
"description": "timer that updates every milisecond",
"init": "now()",
"on": [{"events": {"type": "timer"}, "update": "now()"}]
},
{
"name": "start",
"description": "time in miliseconds at t[0]",
"init": "timer",
"on": [
{
"events": {"signal": "timer"},
"update": "timer > start+duration*1000 ? timer+duration*1000 : start"
},
{"events": {"signal": "duration"}, "update": "timer+duration*1000"}
]
},
{
"name": "end",
"description": "time in miliseconds at t[1]",
"init": "start+duration*1000",
"on": [{"events": {"signal": "start"}, "update": "start+duration*1000"}]
},
{
"name": "direction",
"description": "direction in the oscillation chart. When t is increasing 1 else -1",
"update": "(xOscillationDomain[1]-xOscillationDomain[0])*(timer-start)/(end-start) >= 0 ? 1 : -1"
},
{
"name": "linearOscillationX",
"init": "xOscillationDomain[0]",
"on": [
{
"events": {"signal": "timer"},
"update": "abs(lerp(xOscillationDomain, (timer-start)/(end-start)))"
},
{"events": {"signal": "duration"}, "update": "xOscillationDomain[0]"}
]
},
{
"name": "t",
"description": "current linear t ",
"update": "direction === 1 ? linearOscillationX/xOscillationDomain[1] : 1-linearOscillationX/xOscillationDomain[1]"
},
{
"name": "s",
"description": "current second",
"expr": "direction === 1 ? round(linearOscillationX+0.5) : duration - round(linearOscillationX-0.5)"
},
{"name": "concat_0_width", "update": "350"},
{"name": "concat_1_width", "update": "350"}
],
"spacing": 250,
"bounds": "flush",
"hconcat": [
{
"height": 400,
"view": {"stroke": "transparent"},
"title": {"anchor": "start", "text": "Oscillating Easing"},
"encoding": {
"x": {
"field": "e",
"type": "quantitative",
"scale": {
"domain": [0, 1],
"rangeMin": {"expr": "direction >= 0 ? 0 : concat_0_width"},
"rangeMax": {"expr": "direction >= 0 ? concat_0_width : 0"}
},
"axis": {
"ticks": false,
"labels": false,
"grid": false,
"domain": false,
"title": null
}
},
"y": {
"field": "sort",
"type": "quantitative",
"axis": {
"title": null,
"ticks": false,
"domain": false,
"labelPadding": 20,
"labelFontSize": 13,
"gridOpacity": {"expr": "datum.label%1===0 ? 1 : 0"},
"tickOpacity": {"expr": "datum.label%1===0 ? 1 : 0"},
"labelExpr": "datum.label%1===0 ? data('data_0')[datum.value-1].name : null"
},
"scale": {"zero": false, "reverse": true}
}
},
"layer": [
{
"mark": {
"type": "circle",
"size": 700,
"fillOpacity": 1,
"align": "left",
"fill": {"expr": "colorRange[datum.id-1]"}
}
},
{
"mark": {
"type": "text",
"align": "center",
"baseline": "middle",
"fill": "#fff",
"text": {"expr": "toggleOscillationSecondsVisibility ? datum['seconds'] + 's' : null"},
"fontSize": 12,
"fontWeight": 600
}
},
{
"mark": {
"type": "text",
"dx": {
"expr": "(direction < 0 ? -scale('concat_0_x', datum.e) : -scale('concat_0_x', datum.e))"
},
"dy": 17.5,
"align": "left",
"baseline": "top",
"text": {"expr": "toggleFormulasVisibility ? datum['formula'] : null"},
"fontSize": 12,
"fill": "#777"
}
}
]
},
{
"height": 400,
"view": {"stroke": "transparent"},
"title": {"anchor": "start", "text": "Ease Over t"},
"encoding": {
"color": {
"field": "name",
"type": "ordinal",
"scale": {
"domain": {"expr": "colorDomain"},
"range": [
"#003f5c",
"#ffa600",
"#374c80",
"#ff764a",
"#7a5195",
"#ef5675",
"#bc5090"
]
},
"legend": null
}
},
"layer": [
{
"transform": [
{
"aggregate": [{"op": "count", "as": "t"}],
"groupby": ["id", "name", "formula"]
},
{"calculate": "sequence(0, 1, 0.01)", "as": "t"},
{"flatten": ["t"], "as": ["t"]},
{
"calculate": "datum['name'] === 'Linear' ? datum['t'] : datum['e']",
"as": "e"
},
{
"calculate": "datum['name'] === 'EaseInQuad' ? (datum['t']*datum['t']) : datum['e']",
"as": "e"
},
{
"calculate": "datum['name'] === 'EaseOutQuad' ? datum['t']*(2-datum['t']) : datum['e']",
"as": "e"
},
{
"calculate": "datum['name'] === 'EaseInOutQuad' ? datum['t'] < 0.5 ? 2 * datum['t'] * datum['t'] : -1 + (4 -2 * datum['t']) * datum['t'] : datum['e']",
"as": "e"
},
{
"calculate": "datum['name'] === 'EaseInCubic' ? datum['t'] * datum['t'] * datum['t'] : datum['e']",
"as": "e"
},
{
"calculate": "datum['name'] === 'EaseOutCubic' ? 1 - pow(1 - datum['t'], 3) : datum['e']",
"as": "e"
},
{
"calculate": "datum['name'] === 'EaseInOutCubic' ? datum['t'] < 0.5 ? 4 * datum['t'] * datum['t'] * datum['t'] : (datum['t'] - 1) * (2 * datum['t'] - 2) * (2 * datum['t'] - 2) + 1: datum['e']",
"as": "e"
}
],
"mark": {
"type": "line",
"stroke": {"expr": "colorRange[datum.id-1]"},
"strokeWidth": 2
},
"encoding": {
"x": {
"field": "t",
"type": "quantitative",
"scale": {
"rangeMin": {"expr": "0"},
"rangeMax": {
"expr": "length(data('data_1')) === 0 ? 0 : concat_1_width"
}
},
"axis": {
"tickCount": 10,
"title": "t (original)",
"titleFontSize": 14
}
},
"y": {
"field": "e",
"type": "quantitative",
"axis": {
"tickCount": {"expr": "10"},
"title": "t (eased)",
"titleFontSize": 14
}
}
}
},
{
"mark": {"type": "circle", "size": 75},
"encoding": {
"x": {"field": "t", "type": "quantitative"},
"y": {"field": "e", "type": "quantitative"}
}
}
]
}
],
"data": {
"name": "ease",
"values": [
{"id": 1, "name": "Linear", "formula": "t => t"},
{"id": 2, "name": "EaseInQuad", "formula": "t => t * t"},
{"id": 3, "name": "EaseOutQuad", "formula": "t => t * (2 - t)"},
{
"id": 4,
"name": "EaseInOutQuad",
"formula": "t => t < 0.5 ? 2 * t * t : - 1 + (4 - 2 * t ) * t"
},
{"id": 5, "name": "EaseInCubic", "formula": "t => t * t * t"},
{"id": 6, "name": "EaseOutCubic", "formula": "t => 1 - pow(1 - t, 3)"},
{
"id": 7,
"name": "EaseInOutCubic",
"formula": "t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1"
}
]
},
"transform": [
{"calculate": "null", "as": "value"},
{
"calculate": "datum['name'] === 'Linear' ? t : datum['value']",
"as": "value"
},
{
"calculate": "datum['name'] === 'EaseInQuad' ? (t*t) : datum['value']",
"as": "value"
},
{
"calculate": "datum['name'] === 'EaseOutQuad' ? t*(2-t) : datum['value']",
"as": "value"
},
{
"calculate": "datum['name'] === 'EaseInOutQuad' ? t < 0.5 ? 2 * t * t : -1 + (4 -2 * t) * t : datum['value']",
"as": "value"
},
{
"calculate": "datum['name'] === 'EaseInCubic' ? t * t * t : datum['value']",
"as": "value"
},
{
"calculate": "datum['name'] === 'EaseOutCubic' ? 1 - pow(1 - t, 3) : datum['value']",
"as": "value"
},
{
"calculate": "datum['name'] === 'EaseInOutCubic' ? t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1: datum['value']",
"as": "value"
},
{"filter": "datum['name'] === 'Linear' && !showLinear ? false : true"},
{
"filter": "datum['name'] === 'EaseInQuad' && !showEaseInQuad ? false : true"
},
{
"filter": "datum['name'] === 'EaseOutQuad' && !showEaseOutQuad ? false : true"
},
{
"filter": "datum['name'] === 'EaseInOutQuad' && !showEaseInOutQuad ? false : true"
},
{
"filter": "datum['name'] === 'EaseInCubic' && !showEaseInCubic ? false : true"
},
{
"filter": "datum['name'] === 'EaseOutCubic' && !showEaseOutCubic ? false : true"
},
{
"filter": "datum['name'] === 'EaseInOutCubic' && !showEaseInOutCubic ? false : true"
},
{"calculate": "datum['value']", "as": "e"},
{
"calculate": "xOscillationDomain[1]*(direction > 0 ? datum['value'] : 1-datum['value'])",
"as": "value"
},
{"calculate": "t", "as": "t"},
{"calculate": "s", "as": "seconds"},
{"calculate": "datum['formula']", "as": "y2Label"},
{"window": [{"op": "row_number", "as": "sort"}]}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment