<section class='panel'>
<section class='controls'>
<fieldset class='sub'>
<legend>
axis
<div class='infopop'>the axis along which we skew the element</div>
</legend>
<input name='axis' type='radio' id='x'
class='ac' checked/>
<label for='x'><var>x</var> axis</label>
<input name='axis' type='radio' id='y'
class='ac'/>
<label for='y'><var>y</var> axis</label>
</fieldset>
<section class='sub'>
<label for='a'>
angle
<div class='infopop'>the skew angle</div>
</label>
<input type='range' id='a' min='-89' max='89'/>
</section>
</section>
<pre class='codebox'>transform: skewX(0deg)</pre>
</section>
<section class='panel'>
<svg viewbox='-512 -512 1024 1024'>
<defs>
<path id='sqb'
d='M-256 -256 L256 -256
L240 -240 L-240 -240z' />
</defs>
<g class='initial'>
<g class='square'>
<use class='sqb'
xlink:href='#sqb' />
<use class='sqb' transform='rotate(90)'
xlink:href='#sqb' />
<use class='sqb' transform='rotate(180)'
xlink:href='#sqb' />
<use class='sqb' transform='rotate(270)'
xlink:href='#sqb' />
</g>
<g class='coord-syst'>
<path d='M-500 0 L490 0
L485 -10 L500 0 L485 10 L490 0
M0 -500 L0 490
L-10 485 L0 500 L10 485 L0 490'/>
<text x='500' y='32'
text-anchor='end'>x</text>
<text x='16' y='500'>y</text>
</g>
</g>
<g class='final dir--x'>
<g class='square'>
<use class='sqb'
xlink:href='#sqb' />
<use class='sqb' transform='rotate(90)'
xlink:href='#sqb' />
<use class='sqb' transform='rotate(180)'
xlink:href='#sqb' />
<use class='sqb' transform='rotate(270)'
xlink:href='#sqb' />
</g>
<g class='coord-syst'>
<path class='axis axis--x'
d='M-500 0 L490 0
L485 -10 L500 0 L485 10 L490 0'/>
<path class='axis axis--y'
d='M0 -500 L0 490
L-10 485 L0 500 L10 485 L0 490'/>
<text class='axis--x' x='500' y='32'
text-anchor='end'>x</text>
<text class='axis--y' x='16' y='500'>y</text>
</g>
</g>
<circle cx='192' cy='192' class='point initial' r='8' />
<circle cx='192' cy='192' class='point final' r='8' />
<path class='conn' d='M192 192 L192 192' />
<path class='arc' d='' />
</svg>
</section>
@mixin track() {
width: 14em; height: .375em;
background: #333;
}
@mixin thumb() {
border: none;
border-radius: 0;
width: 1em; height: 1em;
box-shadow: inset 0 0 0 3px #333;
background: #ccc;
}
@mixin thumb-focus() {
background: #4ca454;
}
html { min-width: 15em; }
svg {
display: block;
overflow: visible;
margin: 0 auto;
min-width: 15em; min-height: 15em;
width: 90vmin; height: 90vmin;
}
.controls {
font: 1.125em trebuchet ms, sans-serif;
var { font: 700 italic 1.125em serif; }
}
.initial { opacity: .32; }
.sqb {
.dir--x &:nth-child(even),
.dir--y &:nth-child(odd) {
fill: #be4c39;
}
}
.coord-syst {
stroke: #000;
path { stroke-width: 2; }
.dir--x & .axis--x, .dir--y & .axis--y {
fill: #4472b9;
stroke: #4472b9;
}
.dir--x & .axis--y, .dir--y & .axis--x {
fill: #4ca454;
stroke: #4ca454;
}
text {
font: italic 2em serif;
}
}
.conn, .arc {
fill: darkorange;
stroke: darkorange;
stroke-width: 2;
}
.infopop {
position: absolute;
transform: scale(0);
}
input[type='radio'] {
&, + label { cursor: pointer; }
}
input[type='range'] {
&,
&::slider-runnable-track,
&::slider-thumb {
appearance: none;
}
display: block;
padding: 0;
width: 14em; height: 2em;
background: transparent;
font-size: 1em;
cursor: pointer;
&::slider-runnable-track {
@include track();
}
&::range-track {
@include track();
}
&::track {
@include track();
color: transparent;
}
&::fill-lower,
&::tooltip { display: none; }
&::slider-thumb {
margin-top: -.3125em;
@include thumb();
}
&::range-thumb {
@include thumb();
}
&::thumb {
@include thumb();
}
&:focus {
outline: solid 0 transparent;
&::slider-thumb {
@include thumb-focus();
}
&::range-thumb {
@include thumb-focus();
}
&::thumb {
@include thumb-focus();
}
}
}
.codebox {
min-width: 14.5em;
padding: .5em;
background: #1d1f21;
font: 600 1.375em monospace;
}
.token {
&--property { color: lighten(#9b869c, 20%); }
&--punctuation { color: #ccc; }
&--value { color: lighten(#cd6a51, 20%); }
&--function { color: lighten(#4ca454, 20%); }
&--argument { color: #fff; }
&--number { color: lighten(#e18728, 20%); }
&--unit { color: lighten(#be4c39, 20%); }
}
@media (min-aspect-ratio: 1/1) {
.panel:first-of-type {
float: right;
}
}
@media (max-aspect-ratio: 1/1) {
@media (min-width: 30em) {
.controls {
overflow: hidden;
}
.sub {
float: left;
}
}
@media (min-width: 45em) {
.controls, .codebox {
float: left;
}
.panel:first-of-type {
overflow: hidden;
}
}
}
View Compiled
Object.getOwnPropertyNames(Math).map(function(p) {
window[p] = Math[p];
});
if(!sign) {
var sign = function(n) { return n/abs(n); };
}
var rad = function(val, unit) {
var val = val || 0,
unit = 'deg';
if(unit === 'deg') { return val*PI/180; }
if(unit === 'rad') { return val; }
if(unit === 'turn') { return val*2*PI; }
if(unit === 'grad') { return val*PI/200; }
};
var deg = function(val, unit) {
var val = val || 0,
unit = 'rad';
if(unit === 'deg') { return val; }
if(unit === 'rad') { return val*180/PI; }
if(unit === 'turn') { return val*360; }
if(unit === 'grad') { return val*9/10; }
};
Node.prototype.setAttrs = function(attr_obj) {
for(var prop in attr_obj) {
this.setAttribute(prop, attr_obj[prop]);
}
};
var target = document.querySelector('g.final'),
codebox = document.querySelector('.codebox'),
sar = document.getElementById('a'),
ac = document.querySelectorAll('.ac'),
sel1 = '.point.initial',
p1 = document.querySelector(sel1),
sel2 = '.point.final',
p2 = document.querySelector(sel2),
cl = document.querySelector('.conn'),
arc = document.querySelector('.arc'),
α = rad(sar.value),
axis = 'X', r = 80;
var tokenize = function(str) {
var s = "<span class='token token--",
m = "'>", e = '</span>',
f = ' contenteditable ',
tt = [ /* token types */
'property', 'punctuation', 'value',
'function', 'argument', 'number', 'unit'
],
re = /([a-z]+)(\:)\s*([a-zA-Z]+)(\()(-?[0-9]*\.?[0-9]+)([a-z]*)(\))(;)?/;
str = str.replace(re,
s + tt[0] + m + '$1' + e +
s + tt[1] + m + '$2' + e + ' ' +
s + tt[2] + m +
s + tt[3] + m + '$3' + e +
s + tt[1] + m + '$4' + e +
s + tt[4] + m +
s + tt[5] + "'" + f + '>$5' + e +
s + tt[6] + "'" + f + '>$6' + e + e +
s + tt[1] + m + '$7' + e + e +
s + tt[1] + m + '$8' + e);
return str;
};
var updateAxis = function() {
axis = this.id.toUpperCase();
updateCode();
updateSVG();
};
var updateAngleVal = function() {
α = rad(sar.value);
updateCode();
updateSVG();
};
var updateCode = function() {
var valsel = '.token--value',
valel = codebox.querySelector(valsel);
valel.textContent = 'skew' + axis +
'(' + sar.value + 'deg)';
codebox.innerHTML = tokenize(codebox.textContent);
};
var updateSVG = function() {
var newval = 'skew' + axis +
'(' + sar.value + ')',
sgn = sign(α), d;
target.setAttribute('transform', newval);
if(target.classList) {
if(target.classList.contains('dir--x') &&
axis !== 'X') {
target.classList.remove('dir--x');
target.classList.add('dir--y');
}
if(target.classList.contains('dir--y') &&
axis !== 'Y') {
target.classList.remove('dir--y');
target.classList.add('dir--x');
}
}
else {
if((target.className.baseVal.indexOf('x') !== -1 ||
target.className.animVal.indexOf('x') !== -1) &&
axis !== 'X') {
target.className.baseVal =
target.className.baseVal.replace('x', 'y');
target.className.animVal =
target.className.animVal.replace('x', 'y');
}
if((target.className.baseVal.indexOf('y') !== -1 ||
target.className.animVal.indexOf('y') !== -1) &&
axis !== 'Y') {
target.className.baseVal =
target.className.baseVal.replace('y', 'x');
target.className.animVal =
target.className.animVal.replace('y', 'x');
}
}
if(axis == 'X' && abs(α) > .01) {
d = 'M' + r*sin(α) + ' ' + r*cos(α) +
'A' + r + ' ' + r +
' 0 0 ' + (sgn + 1)/2 + ' 0 ' + r;
if(abs(α) > PI/12) {
d += 'L' + sgn*15 + ' ' + (r - 10) +
'L' + sgn*10 + ' ' + r +
'L' + sgn*15 + ' ' + (r + 10) +
'L0 ' + r +
'A' + r + ' ' + r +
' 0 0 ' + (-sgn + 1)/2 + ' ' +
r*sin(α) + ' ' + r*cos(α);
}
}
if(axis === 'Y' && abs(α) > .01) {
d = 'M' + r*cos(α) + ' ' + r*sin(α) +
'A' + r + ' ' + r +
' 0 0 ' + (-sgn + 1)/2 + ' ' + r + ' 0';
if(abs(α) > PI/12) {
d += 'L' + (r - 10) + ' ' + sgn*15 +
'L' + r + ' ' + sgn*10 +
'L' + (r + 10) + ' ' + sgn*15 +
'L' + r + ' 0' +
'A' + r + ' ' + r +
' 0 0 ' + (sgn + 1)/2 + ' ' +
r*cos(α) + ' ' + r*sin(α);
}
}
arc.setAttribute('d', d);
updatePoint();
};
var updatePoint = function() {
var x, y, a, sgn, d = 'M',
r1 = ~~p1.getAttribute('r'),
r2 = ~~p2.getAttribute('r');
x = ~~p1.getAttribute('cx');
y = ~~p1.getAttribute('cy');
if(axis === 'X') {
a = round(y*tan(α));
sgn = sign(a);
d += (x + sgn*r1) + ' ' + y + 'L';
x += a;
d += (x - sgn*r2) + ' ' + y +
'L' + (x - sgn*(r2 + 15)) + ' ' + (y - 10) +
'L' + (x - sgn*(r2 + 10)) + ' ' + y +
'L' + (x - sgn*(r2 + 15)) + ' ' + (y + 10) +
'L' + (x - sgn*r2) + ' ' + y;
p2.setAttrs({'cx': x, 'cy': y});
cl.setAttribute('d', d);
}
if(axis === 'Y') {
a = round(x*tan(α));
sgn = sign(a);
d += x + ' ' + (y + sgn*r1) + 'L';
y += a;
d += x + ' ' + (y - sgn*r2) +
'L' + (x - 10) + ' ' + (y - sgn*(r2 + 15)) +
'L' + x + ' ' + (y - sgn*(r2 + 10)) +
'L' + (x + 10) + ' ' + (y - sgn*(r2 + 15)) +
'L' + x + ' ' + (y - sgn*r2);
p2.setAttrs({'cx': x, 'cy': y});
cl.setAttribute('d', d);
}
if(cl.getTotalLength() < 75) {
cl.style.opacity = .001;
}
else { cl.style.opacity = .999; }
};
codebox.innerHTML = tokenize(codebox.textContent);
sar.addEventListener('change', updateAngleVal,
false);
sar.addEventListener('input', updateAngleVal,
false);
ac[0].addEventListener('change', updateAxis,
false);
ac[1].addEventListener('change', updateAxis,
false);
This Pen doesn't use any external CSS resources.