From 32f5585a4fcdc96baa3ced78ddb207696215b0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Hurrell?= Date: Mon, 25 Nov 2019 12:36:27 -0300 Subject: [PATCH 1/7] Adding support for css keyframes for animations --- .../CascadingStyleSheetBuilderTest.class.st | 28 ++++++++ .../CssDeclarationBlockTest.class.st | 20 +++++- .../CssKeyframesRuleBuilderTest.class.st | 28 ++++++++ .../CssKeyframesRuleTest.class.st | 25 +++++++ .../CascadingStyleSheetBuilder.class.st | 15 +++++ source/RenoirSt/CssDeclarationBlock.class.st | 66 +++++++++++++++++++ source/RenoirSt/CssKeyframesRule.class.st | 56 ++++++++++++++++ .../RenoirSt/CssKeyframesRuleBuilder.class.st | 44 +++++++++++++ 8 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 source/RenoirSt-Tests/CssKeyframesRuleBuilderTest.class.st create mode 100644 source/RenoirSt-Tests/CssKeyframesRuleTest.class.st create mode 100644 source/RenoirSt/CssKeyframesRule.class.st create mode 100644 source/RenoirSt/CssKeyframesRuleBuilder.class.st diff --git a/source/RenoirSt-Tests/CascadingStyleSheetBuilderTest.class.st b/source/RenoirSt-Tests/CascadingStyleSheetBuilderTest.class.st index 84aeebc..815db89 100644 --- a/source/RenoirSt-Tests/CascadingStyleSheetBuilderTest.class.st +++ b/source/RenoirSt-Tests/CascadingStyleSheetBuilderTest.class.st @@ -58,6 +58,20 @@ CascadingStyleSheetBuilderTest >> testBuildingSimpleStyleSheetWithSomeComments [ self assert: builder build printString equals: '/*Example CSS*/#oop{color: red;}' expandMacros ] +{ #category : #Tests } +CascadingStyleSheetBuilderTest >> testBuildingStyleSheetWithKeyframes [ + + | builder | + + builder := CascadingStyleSheetBuilder new. + builder + declareRuleSetFor: [ :selector | selector class: 'xxx' ] with: [ :style | style margin: 12 pt ]; + declare: [ :cssBuilder | cssBuilder declareRuleSetFor: [ :selector | selector id: #oop ] with: [ :style | style color: #blue ] ] forKeyframesMatching: [ :keyframesBuilder | + keyframesBuilder named: #test ]. + + self assert: builder build printString equals: '.xxx{margin: 12pt;}@keyframes test{#oop{color: blue;}}' expandMacros +] + { #category : #Tests } CascadingStyleSheetBuilderTest >> testBuildingStyleSheetWithMediaQuery [ @@ -83,3 +97,17 @@ CascadingStyleSheetBuilderTest >> testBuildingStyleSheetWithMediaQueryWithoutCon self assert: builder build printString equals: '.xxx{margin: 12pt;}@media all and (width: 600px){#oop{color: red;}}' expandMacros ] + +{ #category : #Tests } +CascadingStyleSheetBuilderTest >> testBuildingStyleSheetWithWebkitKeyframes [ + + | builder | + + builder := CascadingStyleSheetBuilder new. + builder + declareRuleSetFor: [ :selector | selector class: 'xxx' ] with: [ :style | style margin: 12 pt ]; + declare: [ :cssBuilder | cssBuilder declareRuleSetFor: [ :selector | selector id: #oop ] with: [ :style | style color: #blue ] ] forKeyframesMatching: + [ :keyframesBuilder | keyframesBuilder named: #test; enableWebkitCompatibility ]. + + self assert: builder build printString equals: '.xxx{margin: 12pt;}@-webkit-keyframes test{#oop{color: blue;}}' expandMacros +] diff --git a/source/RenoirSt-Tests/CssDeclarationBlockTest.class.st b/source/RenoirSt-Tests/CssDeclarationBlockTest.class.st index 1ea8d6b..46bc883 100644 --- a/source/RenoirSt-Tests/CssDeclarationBlockTest.class.st +++ b/source/RenoirSt-Tests/CssDeclarationBlockTest.class.st @@ -163,6 +163,23 @@ CssDeclarationBlockTest >> testPrintStringOfGeneratedContentProperties [ assert: [ :style | style quotes: {'"<"' . '">"'} ] rendersProperty: 'quotes' withValue: '"<" ">"' ] +{ #category : #Tests } +CssDeclarationBlockTest >> testPrintStringOfKeyframesProperties [ + + self + assert: [ :style | style animationName: 'testAnimation' ] rendersProperty: 'animation-name' withValue: 'testAnimation'; + assert: [ :style | style animationDuration: '5s' ] rendersProperty: 'animation-duration' withValue: '5s'; + assert: [ :style | style animationTimingFunction: 'linear' ] rendersProperty: 'animation-timing-function' withValue: 'linear'; + assert: [ :style | style animationDelay: '5s' ] rendersProperty: 'animation-delay' withValue: '5s'; + assert: [ :style | style animationIterationCount: 2 ] rendersProperty: 'animation-iteration-count' withValue: '2'; + assert: [ :style | style animationDirection: 'normal' ] rendersProperty: 'animation-direction' withValue: 'normal'; + assert: [ :style | style animationFillMode: 'forwards' ] rendersProperty: 'animation-fill-mode' withValue: 'forwards'; + assert: [ :style | style animationPlayState: 'running' ] rendersProperty: 'animation-play-state' withValue: 'running'; + + assert: [ :style | style animation: 'test 5s ease 0s infinite normal backwards running' ] rendersProperty: 'animation' withValue: + 'test 5s ease 0s infinite normal backwards running' +] + { #category : #Tests } CssDeclarationBlockTest >> testPrintStringOfMarginProperties [ @@ -297,7 +314,8 @@ CssDeclarationBlockTest >> testPrintStringOfVisualFormattingProperties [ assert: [ :style | style right: 4 cm ] rendersProperty: 'right' withValue: '4cm'; assert: [ :style | style top: 4 cm ] rendersProperty: 'top' withValue: '4cm'; assert: [ :style | style unicodeBidi: #normal ] rendersProperty: 'unicode-bidi' withValue: 'normal'; - assert: [ :style | style zIndex: 4 ] rendersProperty: 'z-index' withValue: '4' + assert: [ :style | style zIndex: 4 ] rendersProperty: 'z-index' withValue: '4'; + assert: [ :style | style transform: #'rotate(0deg)' ] rendersProperty: 'transform' withValue: 'rotate(0deg)' ] { #category : #Tests } diff --git a/source/RenoirSt-Tests/CssKeyframesRuleBuilderTest.class.st b/source/RenoirSt-Tests/CssKeyframesRuleBuilderTest.class.st new file mode 100644 index 0000000..08847df --- /dev/null +++ b/source/RenoirSt-Tests/CssKeyframesRuleBuilderTest.class.st @@ -0,0 +1,28 @@ +Class { + #name : #CssKeyframesRuleBuilderTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Keyframes' +} + +{ #category : #tests } +CssKeyframesRuleBuilderTest >> testSimpleBuilding [ + + | builder | + builder := CssKeyframesRuleBuilder new. + + self + assert: builder build printString + equals: '@keyframes {}' expandMacros +] + +{ #category : #tests } +CssKeyframesRuleBuilderTest >> testSimpleWebkitCompatibleBuilding [ + + | builder | + builder := CssKeyframesRuleBuilder new. + builder enableWebkitCompatibility. + + self + assert: builder build printString + equals: '@-webkit-keyframes {}' expandMacros +] diff --git a/source/RenoirSt-Tests/CssKeyframesRuleTest.class.st b/source/RenoirSt-Tests/CssKeyframesRuleTest.class.st new file mode 100644 index 0000000..3d778e3 --- /dev/null +++ b/source/RenoirSt-Tests/CssKeyframesRuleTest.class.st @@ -0,0 +1,25 @@ +Class { + #name : #CssKeyframesRuleTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Keyframes' +} + +{ #category : #tests } +CssKeyframesRuleTest >> testPrintStringOfSimpleKeyframes [ + + | styleSheet keyframes | + + styleSheet := CascadingStyleSheet withAll: #(). + keyframes := CssKeyframesRule named: #test enabling: styleSheet withWebkitCompatibilityEnabled: false. + self assert: keyframes printString equals: '@keyframes test{}' expandMacros +] + +{ #category : #tests } +CssKeyframesRuleTest >> testPrintStringOfSimpleWebkitCompatibleKeyframes [ + + | styleSheet keyframes | + + styleSheet := CascadingStyleSheet withAll: #(). + keyframes := CssKeyframesRule named: #test enabling: styleSheet withWebkitCompatibilityEnabled: true. + self assert: keyframes printString equals: '@-webkit-keyframes test{}' expandMacros +] diff --git a/source/RenoirSt/CascadingStyleSheetBuilder.class.st b/source/RenoirSt/CascadingStyleSheetBuilder.class.st index 1d7fb66..f4ffb3a 100644 --- a/source/RenoirSt/CascadingStyleSheetBuilder.class.st +++ b/source/RenoirSt/CascadingStyleSheetBuilder.class.st @@ -28,6 +28,21 @@ CascadingStyleSheetBuilder >> comment: aCommentText [ self addStatement: (CssComment for: aCommentText) ] +{ #category : #Configuring } +CascadingStyleSheetBuilder >> declare: aSubStyleSheetBlock forKeyframesMatching: aKeyframesBlock [ + + | styleSheetBuilder keyframesBuilder | + + styleSheetBuilder := self class new. + aSubStyleSheetBlock value: styleSheetBuilder. + + keyframesBuilder := CssKeyframesRuleBuilder new. + aKeyframesBlock cull: keyframesBuilder. + + keyframesBuilder useStyleSheet: styleSheetBuilder build. + self addStatement: keyframesBuilder build. +] + { #category : #Configuring } CascadingStyleSheetBuilder >> declare: aSubStyleSheetBlock forMediaMatching: aMediaQueryBlock [ diff --git a/source/RenoirSt/CssDeclarationBlock.class.st b/source/RenoirSt/CssDeclarationBlock.class.st index 7965e87..a8c9104 100644 --- a/source/RenoirSt/CssDeclarationBlock.class.st +++ b/source/RenoirSt/CssDeclarationBlock.class.st @@ -11,6 +11,66 @@ Class { #category : #'RenoirSt-Common' } +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animation: aCssValue [ + + "Values must follow the guidelines specified in https://drafts.csswg.org/css-animations/#animation + being like: + animation: name duration timing-function delay iteration-count direction fill-mode play-state + + Always specify the animation-duration property, otherwise the duration is 0, and will never be played." + + self propertyAt: 'animation' put: aCssValue +] + +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animationDelay: aCssValue [ + + self propertyAt: 'animation-delay' put: aCssValue +] + +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animationDirection: aCssValue [ + + self propertyAt: 'animation-direction' put: aCssValue +] + +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animationDuration: aCssValue [ + + self propertyAt: 'animation-duration' put: aCssValue +] + +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animationFillMode: aCssValue [ + + self propertyAt: 'animation-fill-mode' put: aCssValue +] + +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animationIterationCount: aCssValue [ + + self propertyAt: 'animation-iteration-count' put: aCssValue +] + +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animationName: aCssValue [ + + self propertyAt: 'animation-name' put: aCssValue +] + +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animationPlayState: aCssValue [ + + self propertyAt: 'animation-play-state' put: aCssValue +] + +{ #category : #'keyframes properties' } +CssDeclarationBlock >> animationTimingFunction: aCssValue [ + + self propertyAt: 'animation-timing-function' put: aCssValue +] + { #category : #'background properties' } CssDeclarationBlock >> background: aCssValue [ @@ -787,6 +847,12 @@ CssDeclarationBlock >> top: aCssValue [ self propertyAt: 'top' put: aCssValue ] +{ #category : #'visual formatting properties' } +CssDeclarationBlock >> transform: aCssValue [ + + self propertyAt: 'transform' put: aCssValue +] + { #category : #'visual formatting properties' } CssDeclarationBlock >> unicodeBidi: aCssValue [ diff --git a/source/RenoirSt/CssKeyframesRule.class.st b/source/RenoirSt/CssKeyframesRule.class.st new file mode 100644 index 0000000..4da4372 --- /dev/null +++ b/source/RenoirSt/CssKeyframesRule.class.st @@ -0,0 +1,56 @@ +Class { + #name : #CssKeyframesRule, + #superclass : #CssObject, + #instVars : [ + 'keyframeName', + 'styleSheet', + 'webkitCompatibility' + ], + #category : #'RenoirSt-Keyframes' +} + +{ #category : #'Instance Creation' } +CssKeyframesRule class >> named: aName enabling: aCascadingStyleSheet withWebkitCompatibilityEnabled: aBoolean [ + + ^ self new + initializeNamed: aName + enabling: aCascadingStyleSheet + withWebkitCompatibilityEnabled: aBoolean +] + +{ #category : #Printing } +CssKeyframesRule >> appropriateSelector [ + + ^webkitCompatibility ifTrue: [ '@-webkit-keyframes' ] ifFalse: [ '@keyframes' ] +] + +{ #category : #Printing } +CssKeyframesRule >> cssContentOn: aStream [ + aStream + nextPutAll: self appropriateSelector; + space. + keyframeName cssContentOn: aStream. + aStream + newLine; + nextPut: ${. + self cssStatementsContentOn: aStream. + aStream + newLine; + nextPut: $} +] + +{ #category : #Printing } +CssKeyframesRule >> cssStatementsContentOn: aStream [ + | tabStream | + tabStream := IndentOnNewLineWriteStream on: aStream. + tabStream newLine. + styleSheet cssContentOn: tabStream +] + +{ #category : #initialization } +CssKeyframesRule >> initializeNamed: aName enabling: aCascadingStyleSheet withWebkitCompatibilityEnabled: aBoolean [ + + keyframeName := aName. + styleSheet := aCascadingStyleSheet. + webkitCompatibility := aBoolean +] diff --git a/source/RenoirSt/CssKeyframesRuleBuilder.class.st b/source/RenoirSt/CssKeyframesRuleBuilder.class.st new file mode 100644 index 0000000..388e468 --- /dev/null +++ b/source/RenoirSt/CssKeyframesRuleBuilder.class.st @@ -0,0 +1,44 @@ +Class { + #name : #CssKeyframesRuleBuilder, + #superclass : #Object, + #instVars : [ + 'keyframesName', + 'styleSheet', + 'webkitCompatibility' + ], + #category : #'RenoirSt-Keyframes' +} + +{ #category : #Building } +CssKeyframesRuleBuilder >> build [ + ^ CssKeyframesRule + named: keyframesName + enabling: styleSheet + withWebkitCompatibilityEnabled: webkitCompatibility +] + +{ #category : #Configuring } +CssKeyframesRuleBuilder >> enableWebkitCompatibility [ + + webkitCompatibility := true +] + +{ #category : #initialization } +CssKeyframesRuleBuilder >> initialize [ + super initialize. + webkitCompatibility := false. + styleSheet := CascadingStyleSheet withAll: #(). + keyframesName := ''. +] + +{ #category : #Configuring } +CssKeyframesRuleBuilder >> named: aName [ + + keyframesName := aName +] + +{ #category : #Configuring } +CssKeyframesRuleBuilder >> useStyleSheet: aCascadingStyleSheet [ + + styleSheet := aCascadingStyleSheet +] From d5a6fa78264d18c2c35275ebb284ab53818a5553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Hurrell?= Date: Tue, 26 Nov 2019 15:36:43 -0300 Subject: [PATCH 2/7] added 'from' and 'to' selectors used in keyframes --- source/RenoirSt/CssSelector.class.st | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/RenoirSt/CssSelector.class.st b/source/RenoirSt/CssSelector.class.st index 21419c9..8fae0ec 100644 --- a/source/RenoirSt/CssSelector.class.st +++ b/source/RenoirSt/CssSelector.class.st @@ -169,6 +169,12 @@ CssSelector >> focus [ ^ CssPseudoClassSelector focusOn: self ] +{ #category : #'as yet unclassified' } +CssSelector >> from [ + + ^self descendantOfType: 'from' +] + { #category : #building } CssSelector >> havingAttribute: aString [ @@ -259,6 +265,12 @@ CssSelector >> target [ ^ CssPseudoClassSelector targetOn: self ] +{ #category : #'as yet unclassified' } +CssSelector >> to [ + + ^self descendantOfType: 'to' +] + { #category : #'building-pseudo classes' } CssSelector >> visited [ From ac3592be9dc1f7add746c7546ce300c8440ac36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Hurrell?= Date: Tue, 26 Nov 2019 15:38:43 -0300 Subject: [PATCH 3/7] Updating Tutorial Part 3... ...with keyframes examples and simple explanations --- docs/tutorial/Tutorial-Part-III.md | 133 +++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/docs/tutorial/Tutorial-Part-III.md b/docs/tutorial/Tutorial-Part-III.md index 61d5d9d..8312a54 100644 --- a/docs/tutorial/Tutorial-Part-III.md +++ b/docs/tutorial/Tutorial-Part-III.md @@ -36,6 +36,139 @@ Note that the important properties must be created by sending the messages to th ##### References: - http://www.w3.org/TR/CSS2/cascade.html#important-rules +## Keyframes + +The `@keyframes` rule specifies the animation code. +The animation is created by gradually changing from one set of CSS styles to another. +During the animation, you can change the set of CSS styles many times. +Specify when the style change will happen in percent, or with the keywords "from" and "to", which is the same as 0% and 100%. 0% is the beginning of the animation, 100% is when the animation is complete. + +A basic keyframe rule consists of specifying just a keyframe with some style rule: +```smalltalk +CascadingStyleSheetBuilder new + declare: [ :cssBuilder | + cssBuilder + declareRuleSetFor: [ :selector | selector from ] + with: [ :style | style backgroundColor: '#f00' ]; + declareRuleSetFor: [ :selector | selector to ] + with: [ :style | style backgroundColor: '#f99' ] ] + forKeyframesMatching: [ :keyframeBuilder | keyframeBuilder named: 'spin' ]; + build. +``` +To use keyframes in the library just send the message `declare:forKeyframesMatching:` to the builder. The first closure is evaluated with an instance of a `CascadingStyleSheetBuilder` and the second one with a builder of keyframes. + +The keyframes builder recieves the name of the animation you want to create, as shown in the previous example. +The style can be built with either the `animation:` shorthand (`animation: name duration timing-function delay iteration-count direction fill-mode play-state`) or with separate animation styles, such as: +- `animationName:` +- `animationDuration:` +- `animationTimingFunction:` +- `animationIterationCount:` +- `animationDirection:` +- `animationPlayState:` +- `animationDelay:` +- `animationFillMode:` + +For example, a more complex animation can be written: + +```smalltalk +CascadingStyleSheetBuilder + new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + animation: 'spin 5s linear infinite'; + "to replace... + animationName: 'spin'; + animationDuration: '5000ms'; + animationIterationCount: 'infinite'; + animationTimingFunction: 'linear';" + background: '#f00'; + width: 100 px; + height: 100 px; + position: 'relative']; + declare: [ :cssBuilder | + cssBuilder + declareRuleSetFor: [ :selector | 0 percent ] + with: [ :style | + style + transform: 'rotate(0deg)'; + background: '#f00' ]; + declareRuleSetFor: [ :selector | 25 percent ] + with: [ :style | + style + transform: 'rotate(90deg)'; + background: '#f99' ]; + declareRuleSetFor: [ :selector | 50 percent ] + with: [ :style | + style + transform: 'rotate(180deg)'; + background: '#b88' ]; + declareRuleSetFor: [ :selector | 75 percent ] + with: [ :style | + style + transform: 'rotate(270deg)'; + background: '#a66' ]; + declareRuleSetFor: [ :selector | 100 percent ] + with: [ :style | + style + transform: 'rotate(360deg)'; + background: '#f00' ] ] + forKeyframesMatching: [ :keyframeBuilder | keyframeBuilder named: 'spin' ]; + build +``` +Evaluating to: + +```css +div +{ + animation: spin 5s linear infinite; + background: #f00; + width: 100px; + height: 100px; + position: relative; +} + +@keyframes spin +{ + 0% + { + transform: rotate(0deg); + background: #f00; + } + + 25% + { + transform: rotate(90deg); + background: #f99; + } + + 50% + { + transform: rotate(180deg); + background: #b88; + } + + 75% + { + transform: rotate(270deg); + background: #a66; + } + + 100% + { + transform: rotate(360deg); + background: #f00; + } +} +``` + +**Tip:** For best browser support, you should always define both the 0% and the 100% selectors. + +**Note:** The `!important` rule is ignored in a keyframe + +##### References: +- https://drafts.csswg.org/css-animations/ + ## Media Queries A `@media` rule specifies the target media types of a set of statements. The `@media` construct allows style sheet rules that apply to various media in the same style sheet. Style rules outside of `@media` rules apply to all media types that the style sheet applies to. At-rules inside `@media` are invalid in CSS2.1. From aecdf802ec61771e2c850a23eabfe549e1e15c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Hurrell?= Date: Tue, 26 Nov 2019 15:39:36 -0300 Subject: [PATCH 4/7] Update Tutorial-TOC.md --- docs/tutorial/Tutorial-TOC.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tutorial/Tutorial-TOC.md b/docs/tutorial/Tutorial-TOC.md index cbdc576..ea65517 100644 --- a/docs/tutorial/Tutorial-TOC.md +++ b/docs/tutorial/Tutorial-TOC.md @@ -16,6 +16,7 @@ Tutorial - Selector Groups - [Part III](Tutorial-Part-III.md ) - Important Rules + - Keyframes - Media Queries - Vendor specific extensions - Font Face rules From 6fa379f18ffab27cacbc4db41f8c49b5baa55081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Hurrell?= Date: Fri, 29 Nov 2019 15:47:16 -0300 Subject: [PATCH 5/7] Made fixes and changes suggested in the code review. Added easing and transformation function support. --- .../CascadingStyleSheetBuilderTest.class.st | 26 ++--- .../CssCubicBezierTest.class.st | 18 ++++ .../CssKeyframesRuleBuilderTest.class.st | 28 ------ .../CssKeyframesRuleTest.class.st | 12 +-- .../CssPerspectiveTest.class.st | 14 +++ .../RenoirSt-Tests/CssRotate3DTest.class.st | 14 +++ source/RenoirSt-Tests/CssRotateTest.class.st | 41 ++++++++ source/RenoirSt-Tests/CssScaleTest.class.st | 59 +++++++++++ source/RenoirSt-Tests/CssSkewTest.class.st | 41 ++++++++ source/RenoirSt-Tests/CssStepsTest.class.st | 23 +++++ .../RenoirSt-Tests/CssTranslateTest.class.st | 59 +++++++++++ .../CascadingStyleSheetBuilder.class.st | 21 ++-- source/RenoirSt/CssCubicBezier.class.st | 35 +++++++ source/RenoirSt/CssKeyframesRule.class.st | 23 ++--- .../RenoirSt/CssKeyframesRuleBuilder.class.st | 44 --------- source/RenoirSt/CssPerspective.class.st | 31 ++++++ source/RenoirSt/CssRotate.class.st | 55 +++++++++++ source/RenoirSt/CssRotate3D.class.st | 39 ++++++++ source/RenoirSt/CssScale.class.st | 94 ++++++++++++++++++ source/RenoirSt/CssSelector.class.st | 12 --- source/RenoirSt/CssSkew.class.st | 56 +++++++++++ source/RenoirSt/CssSteps.class.st | 44 +++++++++ source/RenoirSt/CssTranslate.class.st | 97 +++++++++++++++++++ 23 files changed, 748 insertions(+), 138 deletions(-) create mode 100644 source/RenoirSt-Tests/CssCubicBezierTest.class.st delete mode 100644 source/RenoirSt-Tests/CssKeyframesRuleBuilderTest.class.st create mode 100644 source/RenoirSt-Tests/CssPerspectiveTest.class.st create mode 100644 source/RenoirSt-Tests/CssRotate3DTest.class.st create mode 100644 source/RenoirSt-Tests/CssRotateTest.class.st create mode 100644 source/RenoirSt-Tests/CssScaleTest.class.st create mode 100644 source/RenoirSt-Tests/CssSkewTest.class.st create mode 100644 source/RenoirSt-Tests/CssStepsTest.class.st create mode 100644 source/RenoirSt-Tests/CssTranslateTest.class.st create mode 100644 source/RenoirSt/CssCubicBezier.class.st delete mode 100644 source/RenoirSt/CssKeyframesRuleBuilder.class.st create mode 100644 source/RenoirSt/CssPerspective.class.st create mode 100644 source/RenoirSt/CssRotate.class.st create mode 100644 source/RenoirSt/CssRotate3D.class.st create mode 100644 source/RenoirSt/CssScale.class.st create mode 100644 source/RenoirSt/CssSkew.class.st create mode 100644 source/RenoirSt/CssSteps.class.st create mode 100644 source/RenoirSt/CssTranslate.class.st diff --git a/source/RenoirSt-Tests/CascadingStyleSheetBuilderTest.class.st b/source/RenoirSt-Tests/CascadingStyleSheetBuilderTest.class.st index 815db89..b3317bb 100644 --- a/source/RenoirSt-Tests/CascadingStyleSheetBuilderTest.class.st +++ b/source/RenoirSt-Tests/CascadingStyleSheetBuilderTest.class.st @@ -64,12 +64,16 @@ CascadingStyleSheetBuilderTest >> testBuildingStyleSheetWithKeyframes [ | builder | builder := CascadingStyleSheetBuilder new. - builder + + builder declareRuleSetFor: [ :selector | selector class: 'xxx' ] with: [ :style | style margin: 12 pt ]; - declare: [ :cssBuilder | cssBuilder declareRuleSetFor: [ :selector | selector id: #oop ] with: [ :style | style color: #blue ] ] forKeyframesMatching: [ :keyframesBuilder | - keyframesBuilder named: #test ]. + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | style color: #blue ] ] + forKeyframesNamed: 'test'. - self assert: builder build printString equals: '.xxx{margin: 12pt;}@keyframes test{#oop{color: blue;}}' expandMacros + self assert: builder build printString equals: '.xxx{margin: 12pt;}@keyframes test{0%%{color: blue;}}' expandMacros ] { #category : #Tests } @@ -97,17 +101,3 @@ CascadingStyleSheetBuilderTest >> testBuildingStyleSheetWithMediaQueryWithoutCon self assert: builder build printString equals: '.xxx{margin: 12pt;}@media all and (width: 600px){#oop{color: red;}}' expandMacros ] - -{ #category : #Tests } -CascadingStyleSheetBuilderTest >> testBuildingStyleSheetWithWebkitKeyframes [ - - | builder | - - builder := CascadingStyleSheetBuilder new. - builder - declareRuleSetFor: [ :selector | selector class: 'xxx' ] with: [ :style | style margin: 12 pt ]; - declare: [ :cssBuilder | cssBuilder declareRuleSetFor: [ :selector | selector id: #oop ] with: [ :style | style color: #blue ] ] forKeyframesMatching: - [ :keyframesBuilder | keyframesBuilder named: #test; enableWebkitCompatibility ]. - - self assert: builder build printString equals: '.xxx{margin: 12pt;}@-webkit-keyframes test{#oop{color: blue;}}' expandMacros -] diff --git a/source/RenoirSt-Tests/CssCubicBezierTest.class.st b/source/RenoirSt-Tests/CssCubicBezierTest.class.st new file mode 100644 index 0000000..4de237c --- /dev/null +++ b/source/RenoirSt-Tests/CssCubicBezierTest.class.st @@ -0,0 +1,18 @@ +Class { + #name : #CssCubicBezierTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Easing' +} + +{ #category : #tests } +CssCubicBezierTest >> testCubicBezier [ + + | cubicBezierExpression | + cubicBezierExpression := + CssCubicBezier + firstXAxis: 0.63 + firstYAxis: 0.05 + secondXAxis: 0.43 + secondYAxis: 1.7. + self assert: cubicBezierExpression printString equals: 'cubic-bezier(0.63, 0.05, 0.43, 1.7)' +] diff --git a/source/RenoirSt-Tests/CssKeyframesRuleBuilderTest.class.st b/source/RenoirSt-Tests/CssKeyframesRuleBuilderTest.class.st deleted file mode 100644 index 08847df..0000000 --- a/source/RenoirSt-Tests/CssKeyframesRuleBuilderTest.class.st +++ /dev/null @@ -1,28 +0,0 @@ -Class { - #name : #CssKeyframesRuleBuilderTest, - #superclass : #TestCase, - #category : #'RenoirSt-Tests-Keyframes' -} - -{ #category : #tests } -CssKeyframesRuleBuilderTest >> testSimpleBuilding [ - - | builder | - builder := CssKeyframesRuleBuilder new. - - self - assert: builder build printString - equals: '@keyframes {}' expandMacros -] - -{ #category : #tests } -CssKeyframesRuleBuilderTest >> testSimpleWebkitCompatibleBuilding [ - - | builder | - builder := CssKeyframesRuleBuilder new. - builder enableWebkitCompatibility. - - self - assert: builder build printString - equals: '@-webkit-keyframes {}' expandMacros -] diff --git a/source/RenoirSt-Tests/CssKeyframesRuleTest.class.st b/source/RenoirSt-Tests/CssKeyframesRuleTest.class.st index 3d778e3..6d48169 100644 --- a/source/RenoirSt-Tests/CssKeyframesRuleTest.class.st +++ b/source/RenoirSt-Tests/CssKeyframesRuleTest.class.st @@ -10,16 +10,6 @@ CssKeyframesRuleTest >> testPrintStringOfSimpleKeyframes [ | styleSheet keyframes | styleSheet := CascadingStyleSheet withAll: #(). - keyframes := CssKeyframesRule named: #test enabling: styleSheet withWebkitCompatibilityEnabled: false. + keyframes := CssKeyframesRule named: #test enabling: styleSheet. self assert: keyframes printString equals: '@keyframes test{}' expandMacros ] - -{ #category : #tests } -CssKeyframesRuleTest >> testPrintStringOfSimpleWebkitCompatibleKeyframes [ - - | styleSheet keyframes | - - styleSheet := CascadingStyleSheet withAll: #(). - keyframes := CssKeyframesRule named: #test enabling: styleSheet withWebkitCompatibilityEnabled: true. - self assert: keyframes printString equals: '@-webkit-keyframes test{}' expandMacros -] diff --git a/source/RenoirSt-Tests/CssPerspectiveTest.class.st b/source/RenoirSt-Tests/CssPerspectiveTest.class.st new file mode 100644 index 0000000..fab9dc7 --- /dev/null +++ b/source/RenoirSt-Tests/CssPerspectiveTest.class.st @@ -0,0 +1,14 @@ +Class { + #name : #CssPerspectiveTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Transformation' +} + +{ #category : #tests } +CssPerspectiveTest >> testPerspective [ + + | perspectiveExpression | + perspectiveExpression := CssPerspective of: 100 px. + + self assert: perspectiveExpression printString equals: 'perspective(100px)' +] diff --git a/source/RenoirSt-Tests/CssRotate3DTest.class.st b/source/RenoirSt-Tests/CssRotate3DTest.class.st new file mode 100644 index 0000000..c6180f2 --- /dev/null +++ b/source/RenoirSt-Tests/CssRotate3DTest.class.st @@ -0,0 +1,14 @@ +Class { + #name : #CssRotate3DTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Transformation' +} + +{ #category : #tests } +CssRotate3DTest >> testRotateOnAllAxis [ + + | rotateExpression | + rotateExpression := CssRotate3D onXAxis: 1 yAxis: 0 zAxis: 0 by: 60 deg. + + self assert: rotateExpression printString equals: 'rotate3d(1, 0, 0, 60deg)' +] diff --git a/source/RenoirSt-Tests/CssRotateTest.class.st b/source/RenoirSt-Tests/CssRotateTest.class.st new file mode 100644 index 0000000..cb0f31b --- /dev/null +++ b/source/RenoirSt-Tests/CssRotateTest.class.st @@ -0,0 +1,41 @@ +Class { + #name : #CssRotateTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Transformation' +} + +{ #category : #tests } +CssRotateTest >> testRotateOnXAxis [ + + | rotateExpression | + rotateExpression := CssRotate onXAxisBy: 70 deg. + + self assert: rotateExpression printString equals: 'rotateX(70deg)' +] + +{ #category : #tests } +CssRotateTest >> testRotateOnYAxis [ + + | rotateExpression | + rotateExpression := CssRotate onYAxisBy: 60 deg. + + self assert: rotateExpression printString equals: 'rotateY(60deg)' +] + +{ #category : #tests } +CssRotateTest >> testRotateOnZAxis [ + + | rotateExpression | + rotateExpression := CssRotate onZAxisBy: 60 deg. + + self assert: rotateExpression printString equals: 'rotateZ(60deg)' +] + +{ #category : #tests } +CssRotateTest >> testSimpleRotate [ + + | rotateExpression | + rotateExpression := CssRotate by: 270 deg. + + self assert: rotateExpression printString equals: 'rotate(270deg)' +] diff --git a/source/RenoirSt-Tests/CssScaleTest.class.st b/source/RenoirSt-Tests/CssScaleTest.class.st new file mode 100644 index 0000000..3a85926 --- /dev/null +++ b/source/RenoirSt-Tests/CssScaleTest.class.st @@ -0,0 +1,59 @@ +Class { + #name : #CssScaleTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Transformation' +} + +{ #category : #tests } +CssScaleTest >> testScaleOnAllAxes [ + + | scaleExpression | + scaleExpression := CssScale onXAxisBy: 2 onYAxisBy: 1 andZAxisBy: 2. + + self assert: scaleExpression printString equals: 'scale3d(2, 1, 2)' +] + +{ #category : #tests } +CssScaleTest >> testScaleOnXAndYAxes [ + + | scaleExpression | + scaleExpression := CssScale onXAxisBy: 2 andYAxisBy: 1. + + self assert: scaleExpression printString equals: 'scale(2, 1)' +] + +{ #category : #tests } +CssScaleTest >> testScaleOnXAxis [ + + | scaleExpression | + scaleExpression := CssScale onlyOnXAxisBy: 2. + + self assert: scaleExpression printString equals: 'scaleX(2)' +] + +{ #category : #tests } +CssScaleTest >> testScaleOnYAxis [ + + | scaleExpression | + scaleExpression := CssScale onlyOnYAxisBy: 2. + + self assert: scaleExpression printString equals: 'scaleY(2)' +] + +{ #category : #tests } +CssScaleTest >> testScaleOnZAxis [ + + | scaleExpression | + scaleExpression := CssScale onlyOnZAxisBy: 2. + + self assert: scaleExpression printString equals: 'scaleZ(2)' +] + +{ #category : #tests } +CssScaleTest >> testSimpleScale [ + + | scaleExpression | + scaleExpression := CssScale by: 2. + + self assert: scaleExpression printString equals: 'scale(2)' +] diff --git a/source/RenoirSt-Tests/CssSkewTest.class.st b/source/RenoirSt-Tests/CssSkewTest.class.st new file mode 100644 index 0000000..a2724d7 --- /dev/null +++ b/source/RenoirSt-Tests/CssSkewTest.class.st @@ -0,0 +1,41 @@ +Class { + #name : #CssSkewTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Transformation' +} + +{ #category : #tests } +CssSkewTest >> testSimpleSkew [ + + | skewExpression | + skewExpression := CssSkew by: 45 deg. + + self assert: skewExpression printString equals: 'skew(45deg)' +] + +{ #category : #tests } +CssSkewTest >> testSkewOnX [ + + | skewExpression | + skewExpression := CssSkew onlyOnXAxisBy: 45 deg. + + self assert: skewExpression printString equals: 'skewX(45deg)' +] + +{ #category : #tests } +CssSkewTest >> testSkewOnXandYAxes [ + + | skewExpression | + skewExpression := CssSkew onXAxisBy: 45 deg andYAxisBy: 50 deg. + + self assert: skewExpression printString equals: 'skew(45deg, 50deg)' +] + +{ #category : #tests } +CssSkewTest >> testSkewOnY [ + + | skewExpression | + skewExpression := CssSkew onlyOnYAxisBy: 45 deg. + + self assert: skewExpression printString equals: 'skewY(45deg)' +] diff --git a/source/RenoirSt-Tests/CssStepsTest.class.st b/source/RenoirSt-Tests/CssStepsTest.class.st new file mode 100644 index 0000000..824b970 --- /dev/null +++ b/source/RenoirSt-Tests/CssStepsTest.class.st @@ -0,0 +1,23 @@ +Class { + #name : #CssStepsTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Easing' +} + +{ #category : #tests } +CssStepsTest >> testSimpleSteps [ + + | translateExpression | + translateExpression := CssSteps by: 1. + + self assert: translateExpression printString equals: 'steps(1)' +] + +{ #category : #tests } +CssStepsTest >> testStepsWithDirection [ + + | translateExpression | + translateExpression := CssSteps by: 1 direction: #start. + + self assert: translateExpression printString equals: 'steps(1, start)' +] diff --git a/source/RenoirSt-Tests/CssTranslateTest.class.st b/source/RenoirSt-Tests/CssTranslateTest.class.st new file mode 100644 index 0000000..c98e250 --- /dev/null +++ b/source/RenoirSt-Tests/CssTranslateTest.class.st @@ -0,0 +1,59 @@ +Class { + #name : #CssTranslateTest, + #superclass : #TestCase, + #category : #'RenoirSt-Tests-Transformation' +} + +{ #category : #tests } +CssTranslateTest >> testSimpleTranslate [ + + | translateExpression | + translateExpression := CssTranslate by: 100 px. + + self assert: translateExpression printString equals: 'translate(100px)' +] + +{ #category : #tests } +CssTranslateTest >> testTranslateForAllAxes [ + + | translateExpression | + translateExpression := CssTranslate onXAxisBy: 100 px onYAxisBy: 100 px andZAxisBy: 100 px. + + self assert: translateExpression printString equals: 'translate3d(100px, 100px, 100px)' +] + +{ #category : #tests } +CssTranslateTest >> testTranslateForXandYAxes [ + + | translateExpression | + translateExpression := CssTranslate onXAxisBy: 100 px andYAxisBy: 100 px. + + self assert: translateExpression printString equals: 'translate(100px, 100px)' +] + +{ #category : #tests } +CssTranslateTest >> testTranslateOnXAxis [ + + | translateExpression | + translateExpression := CssTranslate onlyOnXAxisBy: 100 px. + + self assert: translateExpression printString equals: 'translateX(100px)' +] + +{ #category : #tests } +CssTranslateTest >> testTranslateOnYAxis [ + + | translateExpression | + translateExpression := CssTranslate onlyOnYAxisBy: 100 px. + + self assert: translateExpression printString equals: 'translateY(100px)' +] + +{ #category : #tests } +CssTranslateTest >> testTranslateOnZAxis [ + + | translateExpression | + translateExpression := CssTranslate onlyOnZAxisBy: 100 px. + + self assert: translateExpression printString equals: 'translateZ(100px)' +] diff --git a/source/RenoirSt/CascadingStyleSheetBuilder.class.st b/source/RenoirSt/CascadingStyleSheetBuilder.class.st index f4ffb3a..70e54c7 100644 --- a/source/RenoirSt/CascadingStyleSheetBuilder.class.st +++ b/source/RenoirSt/CascadingStyleSheetBuilder.class.st @@ -29,18 +29,13 @@ CascadingStyleSheetBuilder >> comment: aCommentText [ ] { #category : #Configuring } -CascadingStyleSheetBuilder >> declare: aSubStyleSheetBlock forKeyframesMatching: aKeyframesBlock [ +CascadingStyleSheetBuilder >> declare: aSubStyleSheetBlock forKeyframesNamed: aName [ - | styleSheetBuilder keyframesBuilder | + | styleSheetBuilder | styleSheetBuilder := self class new. aSubStyleSheetBlock value: styleSheetBuilder. - - keyframesBuilder := CssKeyframesRuleBuilder new. - aKeyframesBlock cull: keyframesBuilder. - - keyframesBuilder useStyleSheet: styleSheetBuilder build. - self addStatement: keyframesBuilder build. + self addStatement: (CssKeyframesRule named: aName enabling: styleSheetBuilder build) ] { #category : #Configuring } @@ -68,6 +63,16 @@ CascadingStyleSheetBuilder >> declareFontFaceRuleWith: aDeclarationAction [ self addStatement: (CssRuleSet selector: '@font-face' declarations: declarationBlock) ] +{ #category : #Configuring } +CascadingStyleSheetBuilder >> declareKeyframeRuleSetAt: aPercentage with: aStyleSheetBlock [ + + | declarationBlock | + + declarationBlock := CssDeclarationBlock new. + aStyleSheetBlock cull: declarationBlock. + self addStatement: (CssRuleSet selector: aPercentage declarations: declarationBlock comment: '') +] + { #category : #Configuring } CascadingStyleSheetBuilder >> declareRuleSetFor: aSelectorBlock with: aDeclarationAction [ diff --git a/source/RenoirSt/CssCubicBezier.class.st b/source/RenoirSt/CssCubicBezier.class.st new file mode 100644 index 0000000..9c48b8c --- /dev/null +++ b/source/RenoirSt/CssCubicBezier.class.st @@ -0,0 +1,35 @@ +Class { + #name : #CssCubicBezier, + #superclass : #CssFunction, + #instVars : [ + 'values' + ], + #category : #'RenoirSt-Easing' +} + +{ #category : #'Instance Creation' } +CssCubicBezier class >> firstXAxis: firstXAxisNumber firstYAxis: firstYAxisNumber secondXAxis: secondXAxisNumber secondYAxis: secondYAxisNumber [ + ^ self new initializeFirstXAxis: firstXAxisNumber firstYAxis: firstYAxisNumber secondXAxis: secondXAxisNumber secondYAxis: secondYAxisNumber +] + +{ #category : #private } +CssCubicBezier >> cssFunctionParametersContentOn: aWriteStream [ + values + do: [ :value | value cssContentOn: aWriteStream ] + separatedBy: [ + aWriteStream + nextPut: $,; + space ] +] + +{ #category : #private } +CssCubicBezier >> functionName [ + ^ 'cubic-bezier' +] + +{ #category : #Initialization } +CssCubicBezier >> initializeFirstXAxis: firstXAxisNumber firstYAxis: firstYAxisNumber secondXAxis: secondXAxisNumber secondYAxis: secondYAxisNumber [ + + values := OrderedCollection with: firstXAxisNumber with: firstYAxisNumber + with: secondXAxisNumber with: secondYAxisNumber +] diff --git a/source/RenoirSt/CssKeyframesRule.class.st b/source/RenoirSt/CssKeyframesRule.class.st index 4da4372..a993046 100644 --- a/source/RenoirSt/CssKeyframesRule.class.st +++ b/source/RenoirSt/CssKeyframesRule.class.st @@ -3,31 +3,21 @@ Class { #superclass : #CssObject, #instVars : [ 'keyframeName', - 'styleSheet', - 'webkitCompatibility' + 'styleSheet' ], #category : #'RenoirSt-Keyframes' } { #category : #'Instance Creation' } -CssKeyframesRule class >> named: aName enabling: aCascadingStyleSheet withWebkitCompatibilityEnabled: aBoolean [ +CssKeyframesRule class >> named: aName enabling: aCascadingStyleSheet [ - ^ self new - initializeNamed: aName - enabling: aCascadingStyleSheet - withWebkitCompatibilityEnabled: aBoolean -] - -{ #category : #Printing } -CssKeyframesRule >> appropriateSelector [ - - ^webkitCompatibility ifTrue: [ '@-webkit-keyframes' ] ifFalse: [ '@keyframes' ] + ^ self new initializeNamed: aName enabling: aCascadingStyleSheet ] { #category : #Printing } CssKeyframesRule >> cssContentOn: aStream [ aStream - nextPutAll: self appropriateSelector; + nextPutAll: '@keyframes'; space. keyframeName cssContentOn: aStream. aStream @@ -48,9 +38,8 @@ CssKeyframesRule >> cssStatementsContentOn: aStream [ ] { #category : #initialization } -CssKeyframesRule >> initializeNamed: aName enabling: aCascadingStyleSheet withWebkitCompatibilityEnabled: aBoolean [ +CssKeyframesRule >> initializeNamed: aName enabling: aCascadingStyleSheet [ keyframeName := aName. - styleSheet := aCascadingStyleSheet. - webkitCompatibility := aBoolean + styleSheet := aCascadingStyleSheet ] diff --git a/source/RenoirSt/CssKeyframesRuleBuilder.class.st b/source/RenoirSt/CssKeyframesRuleBuilder.class.st deleted file mode 100644 index 388e468..0000000 --- a/source/RenoirSt/CssKeyframesRuleBuilder.class.st +++ /dev/null @@ -1,44 +0,0 @@ -Class { - #name : #CssKeyframesRuleBuilder, - #superclass : #Object, - #instVars : [ - 'keyframesName', - 'styleSheet', - 'webkitCompatibility' - ], - #category : #'RenoirSt-Keyframes' -} - -{ #category : #Building } -CssKeyframesRuleBuilder >> build [ - ^ CssKeyframesRule - named: keyframesName - enabling: styleSheet - withWebkitCompatibilityEnabled: webkitCompatibility -] - -{ #category : #Configuring } -CssKeyframesRuleBuilder >> enableWebkitCompatibility [ - - webkitCompatibility := true -] - -{ #category : #initialization } -CssKeyframesRuleBuilder >> initialize [ - super initialize. - webkitCompatibility := false. - styleSheet := CascadingStyleSheet withAll: #(). - keyframesName := ''. -] - -{ #category : #Configuring } -CssKeyframesRuleBuilder >> named: aName [ - - keyframesName := aName -] - -{ #category : #Configuring } -CssKeyframesRuleBuilder >> useStyleSheet: aCascadingStyleSheet [ - - styleSheet := aCascadingStyleSheet -] diff --git a/source/RenoirSt/CssPerspective.class.st b/source/RenoirSt/CssPerspective.class.st new file mode 100644 index 0000000..b8ec0f6 --- /dev/null +++ b/source/RenoirSt/CssPerspective.class.st @@ -0,0 +1,31 @@ +Class { + #name : #CssPerspective, + #superclass : #CssFunction, + #instVars : [ + 'value' + ], + #category : #'RenoirSt-Transformation' +} + +{ #category : #'Instance Creation' } +CssPerspective class >> of: aCssMeasure [ + + ^ self new initializeOf: aCssMeasure +] + +{ #category : #private } +CssPerspective >> cssFunctionParametersContentOn: aWriteStream [ + + ^ value cssContentOn: aWriteStream +] + +{ #category : #private } +CssPerspective >> functionName [ + ^ 'perspective' +] + +{ #category : #Initialization } +CssPerspective >> initializeOf: aCssMeasure [ + + value := aCssMeasure +] diff --git a/source/RenoirSt/CssRotate.class.st b/source/RenoirSt/CssRotate.class.st new file mode 100644 index 0000000..e57d05b --- /dev/null +++ b/source/RenoirSt/CssRotate.class.st @@ -0,0 +1,55 @@ +Class { + #name : #CssRotate, + #superclass : #CssFunction, + #instVars : [ + 'rotationDegrees', + 'function' + ], + #category : #'RenoirSt-Transformation' +} + +{ #category : #'Instance Creation' } +CssRotate class >> by: aCssMeasure [ + ^ self by: aCssMeasure usingFunction: 'rotate' +] + +{ #category : #'private - Instance Creation' } +CssRotate class >> by: aCssMeasure usingFunction: aFunctionName [ + ^ self new initializeBy: aCssMeasure usingFunction: aFunctionName +] + +{ #category : #'Instance Creation' } +CssRotate class >> onXAxisBy: aCssMeasure [ + + ^ self by: aCssMeasure usingFunction: 'rotateX' +] + +{ #category : #'Instance Creation' } +CssRotate class >> onYAxisBy: aCssMeasure [ + ^ self by: aCssMeasure usingFunction: 'rotateY' +] + +{ #category : #'Instance Creation' } +CssRotate class >> onZAxisBy: aCssMeasure [ + + ^ self by: aCssMeasure usingFunction: 'rotateZ' +] + +{ #category : #private } +CssRotate >> cssFunctionParametersContentOn: aWriteStream [ + + rotationDegrees cssContentOn: aWriteStream. +] + +{ #category : #private } +CssRotate >> functionName [ + + ^function +] + +{ #category : #initialization } +CssRotate >> initializeBy: aCssMeasure usingFunction: aFunctionName [ + + rotationDegrees := aCssMeasure. + function := aFunctionName +] diff --git a/source/RenoirSt/CssRotate3D.class.st b/source/RenoirSt/CssRotate3D.class.st new file mode 100644 index 0000000..0cc3c5a --- /dev/null +++ b/source/RenoirSt/CssRotate3D.class.st @@ -0,0 +1,39 @@ +Class { + #name : #CssRotate3D, + #superclass : #CssFunction, + #instVars : [ + 'values' + ], + #category : #'RenoirSt-Transformation' +} + +{ #category : #'Instance Creation' } +CssRotate3D class >> onXAxis: anXAxisCoordinate yAxis: aYAxisCoordinate zAxis: aZAxisCoordinate by: aCssMeasure [ + + ^ self new initializeOnXAxis: anXAxisCoordinate yAxis: aYAxisCoordinate zAxis: aZAxisCoordinate by: aCssMeasure +] + +{ #category : #private } +CssRotate3D >> cssFunctionParametersContentOn: aWriteStream [ + + values + do: [ :value | value cssContentOn: aWriteStream ] + separatedBy: [ + aWriteStream + nextPut: $,; + space ] +] + +{ #category : #private } +CssRotate3D >> functionName [ + ^ 'rotate3d' +] + +{ #category : #Initialization } +CssRotate3D >> initializeOnXAxis: anXAxisCoordinate yAxis: aYAxisCoordinate zAxis: aZAxisCoordinate by: aCssMeasure [ + values := OrderedCollection + with: anXAxisCoordinate + with: aYAxisCoordinate + with: aZAxisCoordinate + with: aCssMeasure +] diff --git a/source/RenoirSt/CssScale.class.st b/source/RenoirSt/CssScale.class.st new file mode 100644 index 0000000..948b6c1 --- /dev/null +++ b/source/RenoirSt/CssScale.class.st @@ -0,0 +1,94 @@ +Class { + #name : #CssScale, + #superclass : #CssFunction, + #instVars : [ + 'values', + 'function' + ], + #category : #'RenoirSt-Transformation' +} + +{ #category : #'Instance Creation' } +CssScale class >> by: anInteger [ + ^ self onXAxisBy: anInteger andYAxisBy: '' +] + +{ #category : #'private - Instance Creation' } +CssScale class >> firstAxisValue: anInteger secondAxisValue: anotherInteger thirdAxisValue: aThirdInteger usingFunction: aFunctionName [ + ^ self new + initializeFirstAxisValue: anInteger + secondAxisValue: anotherInteger + andThirdAxisValue: aThirdInteger + usingFunction: aFunctionName +] + +{ #category : #'Instance Creation' } +CssScale class >> onXAxisBy: anInteger andYAxisBy: anotherInteger [ + ^ self + firstAxisValue: anInteger + secondAxisValue: anotherInteger + thirdAxisValue: '' + usingFunction: 'scale' +] + +{ #category : #'Instance Creation' } +CssScale class >> onXAxisBy: anInteger onYAxisBy: anotherInteger andZAxisBy: aThirdInteger [ + ^ self + firstAxisValue: anInteger + secondAxisValue: anotherInteger + thirdAxisValue: aThirdInteger + usingFunction: 'scale3d' +] + +{ #category : #'Instance Creation' } +CssScale class >> onlyOnXAxisBy: anInteger [ + ^ self + firstAxisValue: anInteger + secondAxisValue: '' + thirdAxisValue: '' + usingFunction: 'scaleX' +] + +{ #category : #'Instance Creation' } +CssScale class >> onlyOnYAxisBy: anInteger [ + ^ self + firstAxisValue: anInteger + secondAxisValue: '' + thirdAxisValue: '' + usingFunction: 'scaleY' +] + +{ #category : #'Instance Creation' } +CssScale class >> onlyOnZAxisBy: anInteger [ + ^ self + firstAxisValue: anInteger + secondAxisValue: '' + thirdAxisValue: '' + usingFunction: 'scaleZ' +] + +{ #category : #private } +CssScale >> cssFunctionParametersContentOn: aWriteStream [ + + values + do: [ :value | value cssContentOn: aWriteStream ] + separatedBy: [ + aWriteStream + nextPut: $,; + space ] +] + +{ #category : #private } +CssScale >> functionName [ + ^ function +] + +{ #category : #Initialization } +CssScale >> initializeFirstAxisValue: anInteger secondAxisValue: anotherInteger andThirdAxisValue: aThirdInteger usingFunction: aFunctionName [ + + values := OrderedCollection with: anInteger. + function := aFunctionName. + + anotherInteger asString isEmpty ifFalse: [ values add: anotherInteger ]. + aThirdInteger asString isEmpty ifFalse: [ values add: aThirdInteger ] +] diff --git a/source/RenoirSt/CssSelector.class.st b/source/RenoirSt/CssSelector.class.st index 8fae0ec..21419c9 100644 --- a/source/RenoirSt/CssSelector.class.st +++ b/source/RenoirSt/CssSelector.class.st @@ -169,12 +169,6 @@ CssSelector >> focus [ ^ CssPseudoClassSelector focusOn: self ] -{ #category : #'as yet unclassified' } -CssSelector >> from [ - - ^self descendantOfType: 'from' -] - { #category : #building } CssSelector >> havingAttribute: aString [ @@ -265,12 +259,6 @@ CssSelector >> target [ ^ CssPseudoClassSelector targetOn: self ] -{ #category : #'as yet unclassified' } -CssSelector >> to [ - - ^self descendantOfType: 'to' -] - { #category : #'building-pseudo classes' } CssSelector >> visited [ diff --git a/source/RenoirSt/CssSkew.class.st b/source/RenoirSt/CssSkew.class.st new file mode 100644 index 0000000..73a64ea --- /dev/null +++ b/source/RenoirSt/CssSkew.class.st @@ -0,0 +1,56 @@ +Class { + #name : #CssSkew, + #superclass : #CssFunction, + #instVars : [ + 'values', + 'function' + ], + #category : #'RenoirSt-Transformation' +} + +{ #category : #'Instance Creation' } +CssSkew class >> by: aCssMeasure [ + ^ self firstValue: aCssMeasure secondValue: '' usingFunction: 'skew' +] + +{ #category : #'private - Instance Creation' } +CssSkew class >> firstValue: aCssMeasure secondValue: anotherCssMeasure usingFunction: aFunctionName [ + ^ self new initializeFirstValue: aCssMeasure secondValue: anotherCssMeasure usingFunction: aFunctionName +] + +{ #category : #'Instance Creation' } +CssSkew class >> onXAxisBy: aCssMeasure andYAxisBy: anotherCssMeasure [ + ^ self firstValue: aCssMeasure secondValue: anotherCssMeasure usingFunction: 'skew' +] + +{ #category : #'Instance Creation' } +CssSkew class >> onlyOnXAxisBy: aCssMeasure [ + ^ self firstValue: aCssMeasure secondValue: '' usingFunction: 'skewX' +] + +{ #category : #'Instance Creation' } +CssSkew class >> onlyOnYAxisBy: aCssMeasure [ + ^ self firstValue: aCssMeasure secondValue: '' usingFunction: 'skewY' +] + +{ #category : #private } +CssSkew >> cssFunctionParametersContentOn: aWriteStream [ + values + do: [ :value | value cssContentOn: aWriteStream ] + separatedBy: [ + aWriteStream + nextPut: $,; + space ] +] + +{ #category : #private } +CssSkew >> functionName [ + ^ function +] + +{ #category : #Initialization } +CssSkew >> initializeFirstValue: aCssMeasure secondValue: anotherCssMeasure usingFunction: aFunctionName [ + values := OrderedCollection with: aCssMeasure. + function := aFunctionName. + anotherCssMeasure asString isEmpty ifFalse: [ values add: anotherCssMeasure ] +] diff --git a/source/RenoirSt/CssSteps.class.st b/source/RenoirSt/CssSteps.class.st new file mode 100644 index 0000000..16ebce1 --- /dev/null +++ b/source/RenoirSt/CssSteps.class.st @@ -0,0 +1,44 @@ +Class { + #name : #CssSteps, + #superclass : #CssFunction, + #instVars : [ + 'values' + ], + #category : #'RenoirSt-Easing' +} + +{ #category : #'Instance Creation' } +CssSteps class >> by: anInteger [ + ^ self steps: anInteger headingTo: '' +] + +{ #category : #'Instance Creation' } +CssSteps class >> by: anInteger direction: aConstant [ + ^ self steps: anInteger headingTo: aConstant. +] + +{ #category : #'private - Instance Creation' } +CssSteps class >> steps: anInteger headingTo: aConstant [ + ^ self new initialize steps: anInteger headingTo: aConstant +] + +{ #category : #private } +CssSteps >> cssFunctionParametersContentOn: aWriteStream [ + values + do: [ :value | value cssContentOn: aWriteStream ] + separatedBy: [ + aWriteStream + nextPut: $,; + space ] +] + +{ #category : #private } +CssSteps >> functionName [ + ^ 'steps' +] + +{ #category : #Initialization } +CssSteps >> steps: anInteger headingTo: aConstant [ + values := OrderedCollection with: anInteger. + aConstant isEmpty ifFalse: [ values add: aConstant ] +] diff --git a/source/RenoirSt/CssTranslate.class.st b/source/RenoirSt/CssTranslate.class.st new file mode 100644 index 0000000..add0a94 --- /dev/null +++ b/source/RenoirSt/CssTranslate.class.st @@ -0,0 +1,97 @@ +Class { + #name : #CssTranslate, + #superclass : #CssFunction, + #instVars : [ + 'values', + 'function' + ], + #category : #'RenoirSt-Transformation' +} + +{ #category : #'Instance Creation' } +CssTranslate class >> by: aCssMeasure [ + + ^ self onXAxisBy: aCssMeasure andYAxisBy: '' +] + +{ #category : #'private - Instance Creation' } +CssTranslate class >> firstAxisValue: aCssMeasure secondAxisValue: anotherCssMeasure thirdAxisValue: aThirdCssMeasure usingFunction: aFunctionName [ + + ^ self new + initializeFirstAxisValue: aCssMeasure + secondAxisValue: anotherCssMeasure + andThirdAxisValue: aThirdCssMeasure + usingFunction: aFunctionName +] + +{ #category : #'Instance Creation' } +CssTranslate class >> onXAxisBy: aCssMeasure andYAxisBy: anotherCssMeasure [ + ^ self + firstAxisValue: aCssMeasure + secondAxisValue: anotherCssMeasure + thirdAxisValue: '' + usingFunction: 'translate' +] + +{ #category : #'Instance Creation' } +CssTranslate class >> onXAxisBy: aCssMeasure onYAxisBy: anotherCssMeasure andZAxisBy: aThirdCssMeasure [ + ^ self + firstAxisValue: aCssMeasure + secondAxisValue: anotherCssMeasure + thirdAxisValue: aThirdCssMeasure + usingFunction: 'translate3d' +] + +{ #category : #'Instance Creation' } +CssTranslate class >> onlyOnXAxisBy: aCssMeasure [ + ^ self + firstAxisValue: aCssMeasure + secondAxisValue: '' + thirdAxisValue: '' + usingFunction: 'translateX' +] + +{ #category : #'Instance Creation' } +CssTranslate class >> onlyOnYAxisBy: aCssMeasure [ + ^ self + firstAxisValue: aCssMeasure + secondAxisValue: '' + thirdAxisValue: '' + usingFunction: 'translateY' +] + +{ #category : #'Instance Creation' } +CssTranslate class >> onlyOnZAxisBy: aCssMeasure [ + ^ self + firstAxisValue: aCssMeasure + secondAxisValue: '' + thirdAxisValue: '' + usingFunction: 'translateZ' +] + +{ #category : #private } +CssTranslate >> cssFunctionParametersContentOn: aWriteStream [ + + values + do: [ :value | value cssContentOn: aWriteStream ] + separatedBy: [ + aWriteStream + nextPut: $,; + space ] +] + +{ #category : #private } +CssTranslate >> functionName [ + + ^ function +] + +{ #category : #Initialization } +CssTranslate >> initializeFirstAxisValue: aCssMeasure secondAxisValue: anotherCssMeasure andThirdAxisValue: aThirdCssMeasure usingFunction: aFunctionName [ + + values := OrderedCollection with: aCssMeasure. + function := aFunctionName. + + anotherCssMeasure asString isEmpty ifFalse: [ values add: anotherCssMeasure ]. + aThirdCssMeasure asString isEmpty ifFalse: [ values add: aThirdCssMeasure ] +] From bc019c29bfde8357f7ee92b0a1fb5333862d5ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Hurrell?= Date: Fri, 29 Nov 2019 15:52:19 -0300 Subject: [PATCH 6/7] Update Tutorial-Part-III.md --- docs/tutorial/Tutorial-Part-III.md | 115 +++++++++++++++-------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/docs/tutorial/Tutorial-Part-III.md b/docs/tutorial/Tutorial-Part-III.md index 8312a54..14eec36 100644 --- a/docs/tutorial/Tutorial-Part-III.md +++ b/docs/tutorial/Tutorial-Part-III.md @@ -43,21 +43,22 @@ The animation is created by gradually changing from one set of CSS styles to ano During the animation, you can change the set of CSS styles many times. Specify when the style change will happen in percent, or with the keywords "from" and "to", which is the same as 0% and 100%. 0% is the beginning of the animation, 100% is when the animation is complete. +**Tip:** For best browser support, you should always define both the 0% and the 100% selectors. + A basic keyframe rule consists of specifying just a keyframe with some style rule: ```smalltalk -CascadingStyleSheetBuilder new - declare: [ :cssBuilder | +CascadingStyleSheetBuilder new + declare: [ :cssBuilder | cssBuilder - declareRuleSetFor: [ :selector | selector from ] - with: [ :style | style backgroundColor: '#f00' ]; - declareRuleSetFor: [ :selector | selector to ] - with: [ :style | style backgroundColor: '#f99' ] ] - forKeyframesMatching: [ :keyframeBuilder | keyframeBuilder named: 'spin' ]; - build. + declareKeyframeRuleSetAt: 0 percent + with: [ :style | style backgroundColor: #red ]; + declareKeyframeRuleSetAt: 100 percent + with: [ :style | style backgroundColor: #blue ] ] + forKeyframesNamed: 'example'; + build ``` -To use keyframes in the library just send the message `declare:forKeyframesMatching:` to the builder. The first closure is evaluated with an instance of a `CascadingStyleSheetBuilder` and the second one with a builder of keyframes. +To use keyframes in the library just send the message `declare:forKeyframesNamed:` to the builder. The first closure is evaluated with an instance of a `CascadingStyleSheetBuilder`. The second parameter is to give a name to your keyframe rule. -The keyframes builder recieves the name of the animation you want to create, as shown in the previous example. The style can be built with either the `animation:` shorthand (`animation: name duration timing-function delay iteration-count direction fill-mode play-state`) or with separate animation styles, such as: - `animationName:` - `animationDuration:` @@ -71,50 +72,52 @@ The style can be built with either the `animation:` shorthand (`animation: name For example, a more complex animation can be written: ```smalltalk -CascadingStyleSheetBuilder - new - declareRuleSetFor: [ :selector | selector div ] - with: [ :style | - style - animation: 'spin 5s linear infinite'; - "to replace... - animationName: 'spin'; - animationDuration: '5000ms'; - animationIterationCount: 'infinite'; - animationTimingFunction: 'linear';" - background: '#f00'; - width: 100 px; - height: 100 px; - position: 'relative']; - declare: [ :cssBuilder | - cssBuilder - declareRuleSetFor: [ :selector | 0 percent ] - with: [ :style | - style - transform: 'rotate(0deg)'; - background: '#f00' ]; - declareRuleSetFor: [ :selector | 25 percent ] - with: [ :style | - style - transform: 'rotate(90deg)'; - background: '#f99' ]; - declareRuleSetFor: [ :selector | 50 percent ] - with: [ :style | - style - transform: 'rotate(180deg)'; - background: '#b88' ]; - declareRuleSetFor: [ :selector | 75 percent ] - with: [ :style | - style - transform: 'rotate(270deg)'; - background: '#a66' ]; - declareRuleSetFor: [ :selector | 100 percent ] - with: [ :style | - style - transform: 'rotate(360deg)'; - background: '#f00' ] ] - forKeyframesMatching: [ :keyframeBuilder | keyframeBuilder named: 'spin' ]; - build +CascadingStyleSheetBuilder new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + animation: 'spin 5s linear infinite'; + "replacing... + animationName: 'spin'; + animationDuration: 5000 ms; + animationTimingFunction: 'linear'; + animationIterationCount: 'infinite';" + + maxHeight: 80 vh; + fontSize: #larger; + background: '#f00'; + width: 100 px; + height: 100 px; + position: 'relative' ]; + + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | + style + transform: (CssRotate by: 0 deg); + background: '#f00' ]; + declareKeyframeRuleSetAt: 25 percent + with: [ :style | + style + transform: (CssRotate by: 90 deg); + background: '#f99' ]; + declareKeyframeRuleSetAt: 50 percent + with: [ :style | + style + transform: (CssRotate by: 180 deg); + background: '#b88' ]; + declareKeyframeRuleSetAt: 75 percent + with: [ :style | + style + transform: (CssRotate by: 270 deg); + background: '#a66' ]; + declareKeyframeRuleSetAt: 100 percent + with: [ :style | + style + transform: (CssRotate by: 360 deg); + background: '#f00' ] ] + forKeyframesNamed: 'spin' ``` Evaluating to: @@ -122,6 +125,8 @@ Evaluating to: div { animation: spin 5s linear infinite; + max-height: 80vh; + font-size: larger; background: #f00; width: 100px; height: 100px; @@ -162,8 +167,6 @@ div } ``` -**Tip:** For best browser support, you should always define both the 0% and the 100% selectors. - **Note:** The `!important` rule is ignored in a keyframe ##### References: From 7668091b15ff5d5cb8857946ca94c636b63182af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Hurrell?= Date: Fri, 29 Nov 2019 15:53:37 -0300 Subject: [PATCH 7/7] Update Tutorial-Part-I.md --- docs/tutorial/Tutorial-Part-I.md | 351 +++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) diff --git a/docs/tutorial/Tutorial-Part-I.md b/docs/tutorial/Tutorial-Part-I.md index a0b0e53..8a929b6 100644 --- a/docs/tutorial/Tutorial-Part-I.md +++ b/docs/tutorial/Tutorial-Part-I.md @@ -434,6 +434,357 @@ renders as: ```css repeating-radial-gradient(yellow, green); ``` +#### Transforms +Css transforms are a collection of functions that allow you to shape elements in particular ways. +##### Rotation: `rotate()` `rotateX()` `rotateY()` `rotateZ()` `rotate3d()` + +The library provides support for rotation functions, used in animations to move an element around a central point. The rotate expressions are built instantiating `CssRotate` or `CssRotate3D` for 3D rotations. + +Lets see a basic working rotation example: +```smalltalk +CascadingStyleSheetBuilder new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + animation: 'spin 5s linear infinite'; + width: 100 px; + height: 100 px ]; + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | + style + transform: (CssRotate by: 0 deg); + background: #blue ]; + declareKeyframeRuleSetAt: 100 percent + with: [ :style | + style + transform: (CssRotate by: 360 deg); + background: #red ] ] + forKeyframesNamed: 'spin'; + build +``` +Evaluates to: +```css +div +{ + animation: spin 5s linear infinite; + width: 100px; + height: 100px; +} + +@keyframes spin +{ + 0% + { + transform: rotate(0deg); + background: blue; + } + + 100% + { + transform: rotate(360deg); + background: red; + } +} +``` + +##### Translation: `translate()` `translateX()` `translateY()` `translateZ()` `translate3d()` + +The library supports `translate` functions, used to mode the position of an element. To translate an element, instantiate `CssTranslate`. + +Lets see an example: +```smalltalk +CascadingStyleSheetBuilder new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + background: #blue; + animation: 'animation 5s linear infinite'; + width: 100 px; + height: 100 px ]; + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | + style + transform: (CssTranslate by: 100 px) ] ] + forKeyframesNamed: 'animation'; + build. +``` +Evaluates to: +```css +div +{ + background: blue; + animation: animation 5s linear infinite; + width: 100px; + height: 100px; +} + +@keyframes animation +{ + 0% + { + transform: translate(100px); + } +} +``` + +##### Scale: `scale()` `scaleX()` `scaleY()` `scaleZ()` `scale3d()` + +RenoirSt supports scaling functions. You can use them by building an instance of `CssScale`. + +An example can be: +```smalltalk +CascadingStyleSheetBuilder new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + background: #blue; + animation: 'scale 5s linear infinite'; + width: 100 px; + height: 100 px ]; + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | + style transform: (CssScale by: 2) ]; + declareKeyframeRuleSetAt: 100 percent + with: [ :style | + style transform: (CssScale by: 4) ] ] + forKeyframesNamed: 'scale'; + build. +``` +Evaluating to: +```css +div +{ + background: blue; + animation: scale 5s linear infinite; + width: 100px; + height: 100px; +} + +@keyframes scale +{ + 0% + { + transform: scale(2); + } + + 100% + { + transform: scale(4); + } +} +``` + +##### Skew `skew()` `skewX()` `skewY()` + +The library supports the `skew` function, performing a shear transformation (also known as a shear mapping or a transvection), which displaces each point of an element by a given angle in each direction. To build skew functions, instantiate `CssSkew`. + +An example: +```smalltalk +CascadingStyleSheetBuilder new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + background: #blue; + animation: 'skewAnimation 5s linear infinite'; + width: 100 px; + height: 100 px ]; + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | style transform: (CssSkew by: 45 deg) ] ] + forKeyframesNamed: 'skewAnimation'; + build +``` +Evaluates to: +```css +div +{ + background: blue; + animation: skewAnimation 5s linear infinite; + width: 100px; + height: 100px; +} + +@keyframes skewAnimation +{ + 0% + { + transform: skew(45deg); + } +} +``` + +##### Aditional Functions for Transformation +###### Perspective + +The library supports the `perspective` function by instantiating `CssPerspective`. +This function is used to set a perspective when doing a transformation on the Z axis. + +If we extend the previous example to translate in a 3D space, it would be like: +```smalltalk +CascadingStyleSheetBuilder new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + background: #blue; + animation: 'animation 5s linear infinite'; + width: 100 px; + height: 100 px ]; + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | + style + transform: + {(CssPerspective of: 200 px) . + (CssTranslate + onXAxisBy: 100 px + onYAxisBy: 100 px + andZAxisBy: 100 px )} ] ] + forKeyframesNamed: 'animation'; + build. +``` +Evaluating to: +```css +div +{ + background: blue; + animation: animation 5s linear infinite; + width: 100px; + height: 100px; +} + +@keyframes animation +{ + 0% + { + transform: perspective(200px) translate3d(100px, 100px, 100px); + } +} +``` + +#### Easing Functions + +##### Steps + +The library also supports the `steps` function, used in the timing parameter of animation keyframes called `animation-timing-function`. Steps are a timing function that allows us to break an animation or transition into segments. + +A usage example can be: +```smalltalk +CascadingStyleSheetBuilder new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + background: #blue; + animationName: 'scale'; + animationDuration: 5 s; + animationTimingFunction: (CssSteps by: 2); + animationIterationCount: #infinite; + width: 100 px; + height: 100 px ]; + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | + style transform: (CssScale by: 2) ]; + declareKeyframeRuleSetAt: 100 percent + with: [ :style | + style transform: (CssScale by: 4) ] ] + forKeyframesNamed: 'scale'; + build. +``` +Evaluating to: +```css +div +{ + background: blue; + animation-name: scale; + animation-duration: 5s; + animation-timing-function: steps(2); + animation-iteration-count: infinite; + width: 100px; + height: 100px; +} + +@keyframes scale +{ + 0% + { + transform: scale(2); + } + + 100% + { + transform: scale(4); + } +} +``` + +##### Cubic Bezier + +Renoir supports the `cubic-bezier` function, that can be used with the `transition-timing-function` property to control how a transition will change speed over its duration. It also works with the `animation-timing-function` for keyframes. To create your own cubic bezier timing function build an instance with `CssCubicBezier`. + +Here's an example: +```smalltalk +CascadingStyleSheetBuilder new + declareRuleSetFor: [ :selector | selector div ] + with: [ :style | + style + background: #blue; + animationName: 'scale'; + animationDuration: 5 s; + animationTimingFunction: ( + CssCubicBezier + firstXAxis: 0.63 + firstYAxis: 0.05 + secondXAxis: 0.43 + secondYAxis: 1.7); + animationIterationCount: #infinite; + width: 100 px; + height: 100 px ]; + declare: [ :cssBuilder | + cssBuilder + declareKeyframeRuleSetAt: 0 percent + with: [ :style | + style transform: (CssScale by: 2) ]; + declareKeyframeRuleSetAt: 100 percent + with: [ :style | + style transform: (CssScale by: 4) ] ] + forKeyframesNamed: 'scale'; + build. +``` +Evaluating to: +```css +div +{ + background: blue; + animation-name: scale; + animation-duration: 5s; + animation-timing-function: cubic-bezier(0.63, 0.05, 0.43, 1.7); + animation-iteration-count: infinite; + width: 100px; + height: 100px; +} + +@keyframes scale +{ + 0% + { + transform: scale(2); + } + + 100% + { + transform: scale(4); + } +} +``` #### Box Shadows