<div class="wrapper">
	<header>
		<h1>Cubic-bezier reverser</h1>
		<p>Enter the four values of your cubic-bezier curve to find the reversed curve</p>
	</header>
	
	<form action="post">
		<div class="input-group">
			<label for="x1">X1</label>
			<input type="number" id="x1" name="x1" min="0" max="1" step="0.01" value="0.45" />
			<span class="input__error">Please enter a value between 0 and 1</span>
		</div>
		<div class="input-group">
			<label for="y1">Y1</label>
			<input type="number" id="y1" name="y1" min="0" max="1" step="0.01" value="0.25" />
			<span class="input__error">Please enter a value between 0 and 1</span>
		</div>
		<div class="input-group">
			<label for="x2">X2</label>
			<input type="number" id="x2" name="x2" min="0" max="1" step="0.01" value="0.60" />
			<span class="input__error">Please enter a value between 0 and 1</span>
		</div>
		<div class="input-group">
			<label for="y2">Y2</label>
			<input type="number" id="y2" name="y2" min="0" max="1" step="0.01" value="0.95">
			<span class="input__error">Please enter a value between 0 and 1</span>
		</div>
	</form>
	
	<div class="output-group">
		<div class="output-header">
			<h2>Initial</h2>
			
			<code data-codeblock>
			cubic-bezier(<span data-initial>0, 0, 0, 0</span>)
			</code>
			<span class="info">x1, y1, x2, y2</span>
			<button class="copy" data-copy>Copy to clipboard</button>
		</div>
		<figure class="output-curve">
			<svg id="c1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="0" y="0" width="100" height="100" fill="none" /><path fill="none" stroke="#ee7bff" stroke-width="3" d="M0 100C45 89 62 6 100 0"/></svg>
			<figcaption>Initial</figcaption>
		</figure>
		
	</div>
	
	<div class="output-group">
		<div class="output-header">
			<h2>Reversed</h2>
			<code data-codeblock>
			cubic-bezier(<span data-result>1, 1, 1, 1</span>)
		</code>
		<span class="info">(1 - x2), (1 - y2), (1 - x1 ), (1 - y1)</span>
			<button class="copy" data-copy>Copy to clipboard</button>
		</div>
		<figure class="output-curve">
			<svg id="c2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="0" y="0" width="100" height="100" fill="none" /><path fill="none" stroke="#ee7bff" stroke-width="3" d="M5 100C45 89 62 6 100 0"/></svg>
			<figcaption>Reverse</figcaption>
		</figure>
	</div>
	
	<div class="curves">
		
		
		
	</div>
</div>
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700");

* {
	box-sizing: border-box;
}

body {
	font-family: 'Open Sans', sans-serif;
	background-color: #3c3f4f;
	color: #ECEFF1;
}

button {
	background-color: #ed1dd9;
	border: none;
	padding: 10px 20px;
	color: white;
	border-radius: 0.5em;
	cursor: pointer;
	
	&:hover,
	&:focus {
		background-color: darken(#ed1dd9, 20%);
	}
}

h1 {
	font-size: 2.8rem;
	letter-spacing: -0.02em;
}

h2 {
	margin: 0;
	color: #fc99f2;
}

p {
	font-size: 1.25rem;
}

figure {
	margin: 0 20px;
	width: 280px;
}

figcaption {
	margin-top: 5px;
}

label {
	color: #B0BEC5;
	margin-bottom: 5px;
	display: block;
}

.wrapper {
	max-width: 900px;
	margin: 0 auto;
	padding: 40px 20px;
}

form {
	display: flex;
	margin-bottom: 40px;
	position: relative;
	padding-bottom: 40px;
}

input {
	padding: 10px 15px;
	border: none;
	font-size: 1.2rem;
	display: block;
	width: 100%;
}

input:invalid {
	border: 1px solid red;
	background: red;
	color: white;
	
	& + .input__error {
		display: block;
	}
}

.input__error {
	position: absolute;
	bottom: 0;
	left: 0;
	width: 200px;
	font-size: 0.9rem;
	color: red;
	display: none;
	background-color: white;
}

.input-group {
	&:not(:last-child) {
		margin-right: 40px;
	}
}

.output-group {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	margin-bottom: 20px;
	border: 2px solid grey;
	border-radius: 1em;
	padding: 30px;
	
	&:not(:last-child) {
		margin-bottom: 40px;
	}
}

.output-header {
	flex: 1 1 60%;
	
	> *:not(button) {
		width: 100%;
		display: block;
		margin-bottom: 20px;
	}
}

code {
	font-size: 1.3rem;
}

.info {
	font-style: italic;
	color: #78909C;
}

.curves {
	display: flex;
}

.output-curve {
	flex: 0 0 30%;
}

svg {
	width: 100%;
	background-color: #595f7f;
}
const inputs = [...document.querySelectorAll('input')]
const initialCurve = document.querySelector('[data-initial]')
const resultCurve = document.querySelector('[data-result]')
const copyBtns = [...document.querySelectorAll('[data-copy]')]
const codeBlocks = [...document.querySelectorAll('[data-codeblock]')]
const c1 = document.querySelector('#c1 path')
const c2 = document.querySelector('#c2 path')

const copyToClipboard = str => {
  const el = document.createElement('textarea')
  el.value = str;
  document.body.appendChild(el)
  el.select();
  document.execCommand('copy')
  document.body.removeChild(el)
}

const copyText = (e) => {
	const currIndex = copyBtns.indexOf(e.target)
	const textToCopy = codeBlocks[currIndex].textContent
	
	copyToClipboard(textToCopy)
}

const returnNewValues = (array) => {
	const newValues = array.map((el) => (1 - el).toFixed(2))
	return [ newValues[2], newValues[3], newValues[0], newValues[1] ]
}

const createPath = (array) => `M.0 100 c${array[0] * 100}-${array[1] * 100} ${array[2] * 100}-${array[3] * 100} 100-100`

const calculateResult = (e, el) => {
	const initialValues = inputs.map((el) => {
		return el.checkValidity() && el.value > 0 ? parseFloat(el.value).toFixed(2) : 0
	})
	
	const result = returnNewValues(initialValues)

	const pathC1 = createPath(initialValues)
	const pathC2 = createPath(result)

	initialCurve.textContent = initialValues.join(', ')
	resultCurve.textContent = result.join(', ')

	// console.log(pathC1, pathC2)

	// Set the SVG paths
	c1.setAttribute('d', pathC1)
	c2.setAttribute('d', pathC2)
}

calculateResult()

inputs.forEach((el) => {
	el.addEventListener('change', calculateResult)
})

copyBtns.forEach((el) => {
	el.addEventListener('click', copyText)
})

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.