<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;
}
View Compiled
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)
})
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.