Writing style.transform by hand means every animation must know every other: set a rotation and you lose the translation. jsKjs keeps each component per element and rebuilds translate() rotate() scale() skew() on every frame, so you can tween y on hover while a separate tween spins the same element.
Transforms don't trigger layout, which is why they stay smooth on phones. Prefer them to left, top, width and height whenever you can.
View the source for this demo
<div class="tf-grid">
<div class="tf-stage" aria-hidden="true"><div id="tf-card">$jsKjs</div></div>
<div class="tf-ctrls" id="tf-ctrls">
<div class="field"><label for="tf-x">x: <span>0</span></label><input id="tf-x" data-prop="x" type="range" min="-140" max="140" value="0"></div>
<div class="field"><label for="tf-y">y: <span>0</span></label><input id="tf-y" data-prop="y" type="range" min="-80" max="80" value="0"></div>
<div class="field"><label for="tf-rotate">rotate: <span>0</span></label><input id="tf-rotate" data-prop="rotate" type="range" min="-180" max="180" value="0"></div>
<div class="field"><label for="tf-scale">scale: <span>1</span></label><input id="tf-scale" data-prop="scale" type="range" min="0.4" max="1.8" step="0.05" value="1"></div>
<div class="field"><label for="tf-skewX">skewX: <span>0</span></label><input id="tf-skewX" data-prop="skewX" type="range" min="-30" max="30" value="0"></div>
<div class="row"><button type="button" id="tf-spin">Spin 360° (keeps the rest)</button><button type="button" id="tf-reset">Reset</button></div>
</div>
</div>
<pre class="out" id="tf-out"></pre>const state = { x: 0, y: 0, rotate: 0, scale: 1, skewX: 0 };
const show = () => $('#tf-out').text("style.transform → '" + $('#tf-card')[0].style.transform + "'");
$('#tf-ctrls').on('input', 'input[type=range]', async function () {
state[this.dataset.prop] = +this.value;
$(this).prev().find('span').text(this.value);
await $.to('#tf-card', { ...state, duration: .25, ease: 'power2.out' });
show();
});
$('#tf-spin').on('click', async () => {
await $.to('#tf-card', { rotate: state.rotate + 360, duration: .8, ease: 'power2.inOut' }); // only rotate changes
$.to('#tf-card', { rotate: state.rotate, duration: 0 });
show();
});
$('#tf-reset').on('click', async () => {
Object.assign(state, { x: 0, y: 0, rotate: 0, scale: 1, skewX: 0 });
$('#tf-ctrls input[type=range]').each(function () {
$(this).val(state[this.dataset.prop]).prev().find('span').text(state[this.dataset.prop]);
});
await $.to('#tf-card', { ...state, duration: .5, ease: 'back.out' });
show();
});
show();More examples
Hover lift that does not fight a spin
$('.card').hover(
function () { $(this).to({ y: -6, scale: 1.03, duration: .15 }); },
function () { $(this).to({ y: 0, scale: 1, duration: .2 }); });
$.to('.card .icon', { rotate: 360, duration: 2, ease: 'linear' });API
Tween to a state. stagger offsets each element’s start; onComplete fires after the last finishes. Also chainable on sets: $(sel).to(props).
Migrating
rotation is accepted as an alias of rotate). xPercent, yPercent, z, 3D rotations and transformOrigin are not included — set transform-origin in CSS.Related features
Tweens
Animate any numeric CSS property and transforms (x, y, rotate, scale) with $.to().
Open →CoreClasses & CSS
Toggle classes and set styles with addClass(), toggleClass() and css() — including CSS custom properties, which jsKjs reads and writes without adding px..
Open →MotionEasings
Ten built-in easings — linear, ease-in/out, power2, back.out, elastic.out, bounce.out — plotted as curves and playable live.
Open →