The transform-origin
property lets you modify the origin for transformations of an element. For example, the transformation origin of the rotate()
function is the center of rotation. (This property is applied by first translating the element by the negated value of the property, then applying the element's transform, then translating by the property value.)
/* One-value syntax */ transform-origin: 2px; transform-origin: bottom; /* x-offset y-offset */ transform-origin: 3cm 2px; /* x-offset-keyword y-offset */ transform-origin: left 2px; /* x-offset-keyword y-offset-keyword */ transform-origin: right top; /* y-offset-keyword x-offset-keyword */ transform-origin: top right; /* x-offset y-offset z-offset */ transform-origin: 2px 30% 10px; /* x-offset-keyword y-offset z-offset */ transform-origin: left 5px -3px; /* x-offset-keyword y-offset-keyword z-offset */ transform-origin: right bottom 2cm; /* y-offset-keyword x-offset-keyword z-offset */ transform-origin: bottom right 2cm; /* Global values */ transform-origin: inherit; transform-origin: initial; transform-origin: unset;
<div class="grid"> <div class="col"> <div class="row"> <div class="cell"> rotate(<span class="angle">-120</span>deg); <div class="border"> <div class="content"> <div class="center"></div> <div class="tr d2"></div> </div> </div> </div> <div class="cell"> rotatex(<span class="angle">-120</span>deg); <div class="border"> <div class="content"> <div class="center"></div> <div class="tr d3x"></div> </div> </div> </div> <div class="cell"> rotatey(<span class="angle">-120</span>deg); <div class="border"> <div class="content"> <div class="center"></div> <div class="tr d3y"></div> </div> </div> </div> <div class="cell"> rotatez(<span class="angle">-120</span>deg); <div class="border"> <div class="content"> <div class="center"></div> <div class="tr d3z"></div> </div> </div> </div> </div> <div class="cell note"> transform-origin: <input type="text" value="30% top -20px"> (<i>rotate by <input type="number" min="-360" max="360" value="-120"> deg</i>) </div> </div> </div>
html,body { height: 100%; box-sizing: border-box; } .grid { width: 100%; height: 100%; display: flex; background: #EEE; font: 1em monospace; } .row { display: flex; flex: 1 auto; flex-direction: row; flex-wrap: wrap; justify-content: space-between; } .col { display: flex; flex: 1 auto; flex-direction: column; } .cell { box-sizing: border-box; margin: .5em; padding: .5em; background-color: #FFF; overflow: hidden; text-align: center; } .note { background: #fff3d4; padding: 1em; margin: 0 .5em .5em; font: .8em sans-serif; text-align: center; flex: none; } input[type=number] { width: 3rem; } .border { width : 140px; height: 140px; border: 1px solid #E4F0F5; margin: .5rem auto; overflow: hidden; } .content { width : 50px; height: 50px; margin: 44px; border: 1px dotted hsla(0, 100%, 50%, .5); position: relative; } .center { position: absolute; width : 5px; height: 5px; border-radius: 100%; background: red; top : 50%; left: 50%; transform: translate(-3px, -3px); z-index: 1; } .center:before { content: ''; position: absolute; left : 2px; right : 2px; top : -6px; bottom: -6px; background: red; } .center:after { content: ''; position: absolute; left : -6px; right : -6px; top : 2px; bottom: 2px; background: red; } .tr { width : 50px; height: 50px; background: hsla(215, 100%, 50%, 0.3); transition: transform 2s linear; } .tr.d2 { transform: rotate(-120deg); } .tr.d3x { transform: rotatex(-120deg); } .tr.d3y { transform: rotatey(-120deg); } .tr.d3z { transform: rotatez(-120deg); }
window.addEventListener('load', function () { var LENGTH = /^-?\d+(?:em|ex|cap|ch|ic|rem|lh|rlh|vh|vw|vi|vb|vmin|vmax|px|mm|q|cm|in|pt|pc$)/ var LENGTH_PERCENT = /^-?\d+(?:em|ex|cap|ch|ic|rem|lh|rlh|vh|vw|vi|vb|vmin|vmax|px|mm|q|cm|in|pt|pc|%$)/ var KEYX = /^left|center|right$/ var KEYY = /^top|center|bottom$/ var KEYALL = /^left|center|right|top|bottom$/ var INPUT = document.querySelector('input[type=text]') var ANGLE = document.querySelector('input[type=number]') var CENTER= Array.from(document.querySelectorAll('.center')) var TR = Array.from(document.querySelectorAll('.tr')) function update () { INPUT.style.boxShadow = 'none' var val = INPUT.value.trim() var isValid = true var v = val.split(/\s+/) isValid = isValid && (v[2] === undefined || LENGTH.test(v[2])) isValid = isValid && (v[1] === undefined || LENGTH_PERCENT.test(v[1]) || KEYALL.test(v[1])) isValid = isValid && (LENGTH_PERCENT.test(v[0]) || KEYALL.test(v[0])) if (v.length === 2) { var double_key_words = KEYY.test(v[0]) && KEYX.test(v[1]) var ordinary_value = (LENGTH_PERCENT.test(v[0]) || KEYX.test(v[0])) && (LENGTH_PERCENT.test(v[1]) || KEYY.test(v[1])) isValid = isValid && (double_key_words || ordinary_value) } if (!isValid) { val = '50% 50%' if (v[0] !== '') { INPUT.style.boxShadow = '0 0 .5em red' } } TR.forEach(function (tr, index) { tr.style.transformOrigin = val; setTimeout(function () { var pos = window.getComputedStyle(tr).transformOrigin.split(/\s+/); CENTER[index].style.left = pos[0] CENTER[index].style.top = pos[1] }, 0) }) } function rotate() { var value = Math.min(360, Math.max(-360, Number(ANGLE.value) || 0)); document.querySelector('.d2').style.transform = 'rotate(' + value + 'deg)' document.querySelector('.d3x').style.transform = 'rotatex(' + value + 'deg)' document.querySelector('.d3y').style.transform = 'rotatey(' + value + 'deg)' document.querySelector('.d3z').style.transform = 'rotatez(' + value + 'deg)' Array.from(document.querySelectorAll('.angle')).forEach(function (node){ node.innerText = value }) } function mouseenter() { document.querySelector('.tr.d2' ).style.transform = 'rotate(0)' document.querySelector('.tr.d3x').style.transform = 'rotatex(0)' document.querySelector('.tr.d3y').style.transform = 'rotatey(0)' document.querySelector('.tr.d3z').style.transform = 'rotatez(0)' } INPUT.addEventListener('input', update) ANGLE.addEventListener('input', rotate) update() rotate() document.querySelector('.row').addEventListener('mouseenter', mouseenter) document.querySelector('.row').addEventListener('mouseleave', rotate) })
Values that are not explicitly set are reset to their corresponding values.
Initial value | 50% 50% 0 |
---|---|
Applies to | transformable elements |
Inherited | no |
Percentages | refer to the size of bounding box |
Media | visual |
Computed value | for <length> the absolute value, otherwise a percentage |
Animation type | simple list of length, percentage, or calc |
Canonical order | One or two values, with length made absolute and keywords translated to percentages |
Syntax
The transform-origin
property may be specified using one, two, or three values.
- one-value syntax: the value must be any one of:
- a
<length>
- a
<percentage>
- the keywords
left
,center
,right
,top
,bottom
- a
- two-value syntax:
- one value must be a
<length>
, or a<percentage>
, or one of the keywordsleft
,center
,right
- the other value must be a
<length>
, or a<percentage>
, or one of the keywordstop
,center
,bottom
- one value must be a
- three-value syntax:
- the first two values are the same as for two-value syntax
- the third value must be a
<length>
Values
- x-offset
- Is a
<length>
or a<percentage>
describing how far from the left edge of the box the origin of the transform is set. - offset-keyword
- Is one of the
left
,right
,top
,bottom
orcenter
keyword describing the corresponding offset. - y-offset
- Is a
<length>
or a<percentage>
describing how far from the top edge of the box the origin of the transform is set. - x-offset-keyword
- Is one of the
left
,right
orcenter
keyword describing how far from the left edge of the box the origin of the transform is set. - y-offset-keyword
- Is one of the
top
,bottom
orcenter
keyword describing how far from the top edge of the box the origin of the transform is set. - z-offset
- Is a
<length>
(and never a<percentage>
which would make the statement invalid) describing how far from the user eye the z=0 origin is set.
The keywords are convenience shorthands and match the following <percentage>
values:
keyword | value |
---|---|
left |
0% |
center |
50% |
right |
100% |
top |
0% |
bottom |
100% |
Formal syntax
[ <length-percentage> | left | center | right | top | bottom ] | [ [ <length-percentage> | left | center | right ] && [ <length-percentage> | top | center | bottom ] ] <length>?where
<length-percentage> = <length> | <percentage>
Examples
See Using CSS transforms for examples.
Live Examples
Code | Sample |
---|---|
|
<div class="box1"> </div> .box1 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform: none; -webkit-transform: none; -moz-transform: none; -o-transform: none; } |
|
<div class="box2"> </div> .box2 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform: rotate(30deg); -webkit-transform: rotate(30deg); -moz-transform: rotate(30deg); -o-transform: rotate(30deg); } |
|
<div class="box3"> </div> .box3 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform-origin: 0 0; transform: rotate(30deg); -webkit-transform: rotate(30deg); -moz-transform: rotate(30deg); -o-transform: rotate(30deg); } |
|
<div class="box4"> </div> .box4 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform-origin: 100% 100%; transform: rotate(30deg); -webkit-transform: rotate(30deg); -moz-transform: rotate(30deg); -o-transform: rotate(30deg); } |
|
<div class="box5"> </div> .box5 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform-origin: -10em -30em; transform: rotate(30deg); -webkit-transform: rotate(30deg); -moz-transform: rotate(30deg); -o-transform: rotate(30deg); } |
|
<div class="box6"> </div> .box6 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform: scale(1.9); -webkit-transform: scale(1.9); -moz-transform: scale(1.9); -o-transform: scale(1.9); } |
|
<div class="box7"> </div> .box7 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform: scale(1.9); -webkit-transform: scale(1.9); -moz-transform: scale(1.9); -o-transform: scale(1.9); transform-origin: 0 0; -webkit-transform-origin: 0 0; -moz-transform-origin: 0 0; -o-transform-origin: 0 0; } |
|
<div class="box8"> </div> .box8 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform: scale(1.9); -webkit-transform: scale(1.9); -moz-transform: scale(1.9); -o-transform: scale(1.9); transform-origin: 100% -30%; -webkit-transform-origin: 100% -30%; -moz-transform-origin: 100% -30%; -o-transform-origin: 100% -30%; } |
|
<div class="box9"> </div> .box9 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform: skewX(50deg); transform-origin: 100% -30%; -webkit-transform: skewX(50deg); -moz-transform: skewX(50deg); -o-transform: skewX(50deg); } |
|
<div class="box10"> </div> .box10 { margin: 0.5em; width: 3em; height: 3em; border: solid 1px; background-color: palegreen; transform: skewY(50deg); -webkit-transform: skewY(50deg); -moz-transform: skewY(50deg); -o-transform: skewY(50deg); transform-origin: 100% -30%; -webkit-transform-origin: 100% -30%; -moz-transform-origin: 100% -30%; -o-transform-origin: 100% -30%; } |
Specifications
Specification | Status | Comment |
---|---|---|
CSS Transforms Level 1 The definition of 'transform-origin' in that specification. |
Working Draft |
Browser compatibility
Feature | Chrome | Edge | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
Basic support | Yes -webkit- | Yes Yes -webkit- | 16 3.5 -moz- 49 -webkit- | 10 9 -ms- | 12.1 10.5 -o- | 3.1 -webkit- |
Three-value syntax | Yes | ? | 10 | 9 | No | Yes |
Support in SVG | Yes | ? | 432 | No | Yes | ? |
Feature | Android webview | Chrome for Android | Edge mobile | Firefox for Android | IE mobile | Opera Android | iOS Safari |
---|---|---|---|---|---|---|---|
Basic support | ? | ? | Yes Yes -webkit- | 16 4 -moz- 49 -webkit- | 8.1 -webkit- | ? | ? |
Three-value syntax | ? | ? | ? | ? | ? | No | ? |
Support in SVG | ? | ? | ? | ? | ? | ? | ? |
1. From version 44: this feature is behind the layout.css.prefixes.webkit
preference (needs to be set to true
). To change preferences in Firefox, visit about:config.
2. Keywords and percentages refer to the canvas instead of the object itself. See bug 1209061.
3. From version 41: this feature is behind the svg.transform-origin.enabled
preference (needs to be set to true
). To change preferences in Firefox, visit about:config.