-
Notifications
You must be signed in to change notification settings - Fork 4
Support for basic shape circle % #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<!-- <!DOCTYPE html> | ||
<script src="../third_party/web-animations-js/web-animations.dev.js"></script> | ||
<script src="../motion-path-polyfill.min.js"></script> | ||
|
||
<div id="container"> | ||
<div id="target"> | ||
<img src="Nyancat.png" height="10%" width="10%"> | ||
</div> | ||
</div> | ||
|
||
<style> | ||
body { | ||
margin: 0px; | ||
} | ||
|
||
#container { | ||
height: 100px; | ||
width: 100px; | ||
} | ||
#target { | ||
position: absolute; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't think we need this. |
||
} | ||
|
||
</style> | ||
|
||
<script> | ||
var keyframes = [ {offsetPath: 'circle(100px at 500px 500px)', offsetDistance: '0%'}, | ||
{offsetPath: 'circle(100px at 500px 500px)', offsetDistance: '100%'}]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The demo should test percentages. I suspect it will fail given the dependency you have on the element during initial keyframe parsing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Although that issue is currently hidden by the soon to be removed startKeyframe == endKeyframe optimisation. |
||
var timing = {duration: 20000, iterations: 3}; | ||
target.animate(keyframes, timing); | ||
</script> --> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,12 @@ | |
'use strict'; | ||
|
||
(function () { | ||
var isNumeric = internalScope.isNumeric; | ||
|
||
function roundToHundredth (number) { | ||
return Math.round(number * 100) / 100; | ||
} | ||
|
||
function basicShapePolygonParse (input, element) { | ||
// TODO: Support the fill-rule option and % | ||
var argumentList = input.split(','); | ||
|
@@ -50,37 +56,127 @@ | |
return {type: 'path', path: path}; | ||
} | ||
|
||
function basicShapeCircleParse (input, element) { | ||
// TODO: Need element as an argument to this function | ||
var radius; | ||
var position = /at (.*?)$/.exec(input); | ||
function getCirclePathPosition (parentProperties, position) { | ||
var analysedPosition = []; | ||
|
||
if (position !== null) { | ||
var positionList = position[1].split(/\s+/); | ||
|
||
// If only one value is specified, the second value is assumed to be 'center' | ||
// https://drafts.csswg.org/css-backgrounds-3/#position | ||
for (var index in positionList) { | ||
var aPosition = positionList[index]; | ||
|
||
var aPositionUnit = /(%|px)$/.exec(aPosition)[1]; | ||
if (aPositionUnit === null) { | ||
return null; | ||
} | ||
|
||
var aPositionValueString = aPosition.substring(0, aPosition.length - aPositionUnit.length); | ||
if (!isNumeric(aPositionValueString) || aPositionValueString === '') { | ||
return null; | ||
} | ||
|
||
var aPositionValue = Number(aPositionValueString); | ||
if (aPositionUnit === '%') { | ||
if (Number(index) === 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To get rid of magic numbers you could possibly set a boolean flag outside of the for loop. E.g var isWidth = true |
||
if (!parentProperties || !parentProperties.width) { | ||
return null; | ||
} | ||
aPositionValue *= (parentProperties.width / 100); | ||
} | ||
if (Number(index) === 1) { | ||
if (!parentProperties || !parentProperties.height) { | ||
return null; | ||
} | ||
aPositionValue *= (parentProperties.height / 100); | ||
} | ||
} | ||
analysedPosition[index] = aPositionValue; | ||
} | ||
} | ||
|
||
// TODO: Need to support other positions as currently this only supports positions in which both x and y are specified and are in px | ||
if (analysedPosition.length < 2) { | ||
for (var i = analysedPosition.length; i < 2; i++) { | ||
if (Number(i) === 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could maybe use the bool flag idea here again |
||
if (!parentProperties || !parentProperties.width) { | ||
return null; | ||
} | ||
analysedPosition[i] = parentProperties.width / 2; | ||
} else if (Number(i) === 1) { | ||
if (!parentProperties || !parentProperties.height) { | ||
return null; | ||
} | ||
analysedPosition[i] = parentProperties.height / 2; | ||
} | ||
} | ||
} | ||
|
||
return analysedPosition; | ||
} | ||
|
||
function getCirclePathRadius (parentProperties, position, input) { | ||
var radiusString; | ||
if (position === null) { | ||
// TODO: Set default position to the center of the reference box | ||
position = '0px 0px'; | ||
if (input !== '') { | ||
radius = input; | ||
radiusString = input; | ||
} | ||
} else { | ||
position = position[1].split(/\s+/); | ||
radius = (/^(.*?) at/.exec(input)); | ||
if (radius === null) { | ||
radius = 'closest-side'; | ||
radiusString = (/^(.*?) at/.exec(input)); | ||
// TODO: Add support for when a radius had not been specified | ||
if (radiusString === null) { | ||
radiusString = 'closest-side'; | ||
} else { | ||
radius = radius[1]; | ||
radiusString = radiusString[1]; | ||
} | ||
} | ||
|
||
radius = Number(radius.substring(0, radius.length - 2)); | ||
var radiusUnit = /(%|px)$/.exec(radiusString); | ||
if (radiusUnit === null) { | ||
return null; | ||
} | ||
|
||
var radiusValueString = radiusString.substring(0, radiusString.length - radiusUnit[1].length); | ||
if (!isNumeric(radiusValueString)) { | ||
return null; | ||
} | ||
var radiusValue = Number(radiusValueString); | ||
|
||
if (radiusUnit[1] === '%') { | ||
var height = parentProperties.height; | ||
var width = parentProperties.width; | ||
if (!height || !width) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should just be checking !parentProperties. |
||
return null; | ||
} | ||
|
||
var positionX = Number(position[0].substring(0, position[0].length - 2)); | ||
var positionY = Number(position[1].substring(0, position[1].length - 2)); | ||
return roundToHundredth((Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) / Math.sqrt(2)) * radiusValue / 100); | ||
} | ||
|
||
return Number(radiusValueString); | ||
} | ||
|
||
function basicShapeCircleParse (input, element) { | ||
var parentProperties = null; | ||
if (element) { | ||
parentProperties = element.offsetParent ? element.offsetParent.getBoundingClientRect() : null; | ||
} | ||
|
||
var position = /at (.*?)$/.exec(input); | ||
|
||
var radiusValue = getCirclePathRadius(parentProperties, position, input); | ||
if (!radiusValue) { | ||
return undefined; | ||
} | ||
|
||
var analysedPosition = getCirclePathPosition(parentProperties, position); | ||
if (!analysedPosition) { | ||
return undefined; | ||
} | ||
|
||
var pathString = 'M ' + positionX + ' ' + positionY + | ||
' m 0,' + (-radius) + | ||
' a ' + radius + ',' + radius + ' 0 0,1 ' + radius + ',' + radius + | ||
' a ' + radius + ',' + radius + ' 0 1,1 ' + (-radius) + ',' + (-radius) + ' z'; | ||
var pathString = 'M ' + analysedPosition[0] + ' ' + analysedPosition[1] + | ||
' m 0,' + (-radiusValue) + | ||
' a ' + radiusValue + ',' + radiusValue + ' 0 0,1 ' + radiusValue + ',' + radiusValue + | ||
' a ' + radiusValue + ',' + radiusValue + ' 0 1,1 ' + (-radiusValue) + ',' + (-radiusValue) + ' z'; | ||
|
||
return {type: 'path', path: pathString}; | ||
} | ||
|
@@ -166,6 +262,7 @@ | |
return undefined; | ||
} | ||
var toParse = [basicShapePolygonParse, basicShapeCircleParse, basicShapeInsetParse, basicShapeEllipseParse]; | ||
// var toParse = [basicShapeCircleParse]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not needed anymore 😄 |
||
for (var i = 0; i < toParse.length; i++) { | ||
var result = toParse[i](shapeArguments[1], element); | ||
if (result) { | ||
|
@@ -228,4 +325,5 @@ | |
|
||
internalScope.offsetPathParse = offsetPathParse; | ||
internalScope.offsetPathMerge = offsetPathMerge; | ||
internalScope.roundToHundredth = roundToHundredth; | ||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,97 @@ | ||
/* global suite test internalScope */ | ||
/* global assert suite test internalScope */ | ||
|
||
(function () { | ||
suite('offsetPath', function () { | ||
test('basicShapeCircle', function () { | ||
var assertTransformInterpolation = internalScope.assertTransformInterpolation; | ||
(function () { | ||
suite('offsetPath', function () { | ||
test('basicShapeCircle', function () { | ||
var assertTransformInterpolation = internalScope.assertTransformInterpolation; | ||
var offsetPathParse = internalScope.offsetPathParse; | ||
|
||
assertTransformInterpolation([ | ||
var containerStyle = { | ||
position: 'absolute', | ||
height: '100px', | ||
width: '200px' | ||
}; | ||
|
||
var container = document.createElement('div'); | ||
|
||
for (var property in containerStyle) { | ||
container.style[property] = containerStyle[property]; | ||
} | ||
|
||
var target = document.createElement('div'); | ||
container.appendChild(target); | ||
document.body.appendChild(container); | ||
|
||
var circlePathString = offsetPathParse('circle(50%)', target).path; | ||
assert.equal(circlePathString, 'M 100 50 m 0,-79.06 a 79.06,79.06 0 0,1 79.06,79.06 a 79.06,79.06 0 1,1 -79.06,-79.06 z'); | ||
|
||
circlePathString = offsetPathParse('circle(10px)', target).path; | ||
assert.equal(circlePathString, 'M 100 50 m 0,-10 a 10,10 0 0,1 10,10 a 10,10 0 1,1 -10,-10 z'); | ||
|
||
circlePathString = offsetPathParse('circle(10px at 50%)', target).path; | ||
assert.equal(circlePathString, 'M 100 50 m 0,-10 a 10,10 0 0,1 10,10 a 10,10 0 1,1 -10,-10 z'); | ||
|
||
circlePathString = offsetPathParse('circle(50% at 100px 200px)', target).path; | ||
assert.equal(circlePathString, 'M 100 200 m 0,-79.06 a 79.06,79.06 0 0,1 79.06,79.06 a 79.06,79.06 0 1,1 -79.06,-79.06 z'); | ||
|
||
circlePathString = offsetPathParse('circle(10px at 50% 50%)', target).path; | ||
assert.equal(circlePathString, 'M 100 50 m 0,-10 a 10,10 0 0,1 10,10 a 10,10 0 1,1 -10,-10 z'); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should add a test for garbage input to circle |
||
assertTransformInterpolation([ | ||
{'offsetPath': 'circle(10px at 0px 0px)', 'offsetDistance': '0%'}, | ||
{'offsetPath': 'circle(10px at 0px 0px)', 'offsetDistance': '100%'}], | ||
[ | ||
[ | ||
{at: 0, is: 'translate3d(0px, -10px, 0px)'}, | ||
{at: 0.25, is: 'translate3d(10px, 0px, 0px) rotate(90deg)'}, | ||
{at: 0.5, is: 'translate3d(0px, 10px, 0px) rotate(180deg)'}, | ||
{at: 0.75, is: 'translate3d(-10px, 0px, 0px) rotate(-90deg)'}, | ||
{at: 1, is: 'translate3d(0px, -10px, 0px)'} | ||
] | ||
] | ||
); | ||
|
||
assertTransformInterpolation([ | ||
assertTransformInterpolation([ | ||
{'offsetPath': 'circle(10px at 0px 0px)', 'offsetDistance': '0px'}, | ||
{'offsetPath': 'circle(10px at 0px 0px)', 'offsetDistance': '62.83px'}], | ||
[ | ||
[ | ||
{at: 0, is: 'translate3d(0px, -10px, 0px)'}, | ||
{at: 0.25, is: 'translate3d(10px, 0px, 0px) rotate(89.45deg)'}, | ||
{at: 0.5, is: 'translate3d(0.01px, 10px, 0px) rotate(180deg)'}, | ||
{at: 0.75, is: 'translate3d(-10px, 0.01px, 0px) rotate(-90deg)'}, | ||
{at: 1, is: 'translate3d(-0.01px, -10px, 0px) rotate(-0.55deg)'} | ||
] | ||
] | ||
); | ||
|
||
assertTransformInterpolation([ | ||
assertTransformInterpolation([ | ||
{'offsetPath': 'circle(10px at 0px 0px)', 'offsetDistance': '0%'}, | ||
{'offsetPath': 'circle(10px at 0px 0px)', 'offsetDistance': '50%'}], | ||
[ | ||
[ | ||
{at: 0, is: 'translate3d(0px, -10px, 0px)'}, | ||
{at: 0.5, is: 'translate3d(10px, 0px, 0px) rotate(90deg)'}, | ||
{at: 1, is: 'translate3d(0px, 10px, 0px) rotate(180deg)'} | ||
] | ||
] | ||
); | ||
|
||
assertTransformInterpolation([ | ||
assertTransformInterpolation([ | ||
{'offsetPath': 'circle(10px at 0px 0px)', 'offsetDistance': '0px'}, | ||
{'offsetPath': 'circle(10px at 0px 0px)', 'offsetDistance': '31.42px'}], | ||
[ | ||
[ | ||
{at: 0, is: 'translate3d(0px, -10px, 0px)'}, | ||
{at: 0.5, is: 'translate3d(10px, 0px, 0px) rotate(90deg)'}, | ||
{at: 1, is: 'translate3d(0px, 10px, 0px) rotate(179.45deg)'} | ||
] | ||
] | ||
); | ||
|
||
assertTransformInterpolation([ | ||
assertTransformInterpolation([ | ||
{'offsetPath': 'circle(10px at 100px 100px)', 'offsetDistance': '0%'}, | ||
{'offsetPath': 'circle(10px at 100px 100px)', 'offsetDistance': '100%'}], | ||
[ | ||
[ | ||
{at: 0, is: 'translate3d(100px, 90px, 0px)'}, | ||
{at: 0.25, is: 'translate3d(110px, 100px, 0px) rotate(90deg)'}, | ||
{at: 0.5, is: 'translate3d(100px, 110px, 0px) rotate(180deg)'}, | ||
{at: 0.75, is: 'translate3d(90px, 100px, 0px) rotate(-90deg)'}, | ||
{at: 1, is: 'translate3d(100px, 90px, 0px)'} | ||
] | ||
] | ||
); | ||
}); | ||
}); | ||
})(); | ||
}); | ||
}); | ||
})(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kill the cat