The documentation is in a separate file because we ship transitionAnimation.js as part of the SDK, so any comments there are visible to the public.
Animations do not affect the persistent properties of an object. When the animation completes, the object's properties return to their original values. Some animations base their initial properties on the object's current properties. For example, enterPage animates an element's position by first displacing it by an offset, and then gradually moving it to its normal position. To avoid discontinuity, you should set the object's final properties to match the final properties of the animation.
Transitions change an object's properties from an old value to a new value. For example, a reposition transition visually moves an object from one position to another.
Animations temporarily alter an object's current properties, and eventually return them to their original values. For example, an enterPage animation takes an element on the screen, temporarily offsets its position, then gradually animates the element back to its original position.
The distinction between animations and transition is that animations could in theory all be removed from the application without any change to the logical behavior of the program. (The object still appears; it just doesn't look as pretty.) On the other hand, transitions represent state changes and would have to be replaced with some other way of changing the visual state. (The object needs to move from one place to another regardless.)
A large drawback with animations are that they require keyframes defined in css. For animations which have static keyframes we shipped them hard coded in css. However other ones we create by creating a style tag. In Windows Blue the performance hit of creating a style tag is much larger because it is impacted by the number of elements and in Blue the apps are more complex and also the ListView control does not virtualize the win-container elements. We added an options argument to the showEdgeUI and hideEdgeUI animations so that we can use transitions instead of animations due to this performance impact.
From a programmatic standpoint, transitions and animations are very similar. For the purpose of this document, we will use the word action to mean "transition or animation".
| Characteristic | Transition | Animation |
| Lifetime | -ms-transition-property | -ms-animation-name |
| Target property | -ms-transition-property | @keyframe |
| Target values | implied | @keyframe |
| Initial delay | -ms-transition-delay | -ms-animation-delay |
| Duration | -ms-transition-duration | -ms-animation-duration |
| Curve | -ms-transition-timing-function | -ms-animation-timing-function |
| Direction | always animates forward | -ms-animation-direction |
| Iteration | Executes only once | -ms-animation-iteration-count |
| Persistence | always persistent | -ms-animation-fill-mode (sort-of) |
| Completion event | transitionend | animationend |
All of the properties are comma-separated lists, corresponding to respective actions. For example, to apply three actions to an element, set each of the properties to three values, separated by commas. Rules exist if one list is shorter than another; we will not take advantage of those rules, although we need to guard against the possibility that the host application may have.
(Note that the items in the list are reported by Internet Explorer as separated by a comma and a space. This is important to bear in mind when trying to parse the list back out.)
For both transitions and animations, the properties other than the lifetime property are captured at the moment the action becomes active. After the action becomes active, changes to properties other than the lifetime property are ignored. This capture behavior is essential to our design.
The W3C specifies that script-applied actions become active when the style is resolved. (Earlier versions of the specification were unclear.) See Resolving styles for further discussion of style resolution.
Our overall algorithm for applying actions is as follows. Detailed discussion of each step follows the outline.
It is important to append the new values to the existing properties, because the W3C says that in case of conflict, the last one wins. We set the new values via the shorthand property, because that solves multiple problems:
Note that you cannot read the action shorthand property: In the case where the non-lifetime action properties have the wrong number of commas, the shorthand property reads back as an empty string. (This behavior is required by the CSS specification.) Instead, we read the action-lifetime property and use just the names without any of the additional properties. This works because of the capture behavior described above: The parameters of the existing actions were already captured, so the values we provide here are irrelevant.
For example, consider the following properties. Observe that although there are two elements in the lifetime property, there are three in the duration (too many), only one in the timing-function (too few), and two in the delay property (just right).
| Property | Value |
| lifetime | "a, b" |
| duration | "0ms, 300ms, 500ms" |
| timing-function | "ease-in" |
| delay | "0ms, 0ms" |
The CSS specification describes what happens when there are missing or extra parameters. The details do not affect us.
Suppose we want to start a "c" action with 100ms duration, linear timing, and no delay; and we also want to start a "d" action with 100ms duration, linear timing, and 100ms delay. We first read the lifetime property, then append the shorthand description of the new action:
style.lifetime = "a, b, c 100ms linear 0ms, d 100ms linear 100ms"
The browser parses this and sets multiple properties based on the shorthand:
| Property | Value |
| lifetime | "a, b, c, d" |
| duration | "0ms, 0ms, 100ms, 100ms" |
| timing-function | "ease, ease, linear, linear" |
| delay | "0ms, 0ms, 0ms, 100ms" |
Providing a name with no auxiliary properties is legal shorthand; it means that all other properties receive default values. (Which are ignored, since the action is proceeding with the captured values.)
The first animation to complete is "c", and we clean it up by deleting its name from the lifetime property.
| Property | Value |
| lifetime | "a, b, d" |
| duration | "0ms, 0ms, 100ms, 100ms" |
| timing-function | "ease, ease, linear, linear" |
| delay | "0ms, 0ms, 0ms, 100ms" |
Note that the other properties remain unchanged, but that's okay, because their values were captured when the animation started.
When the "d" animation completes, we realize that this is the last PVL animation, so we restore all the properties to their original values.
| Property | Value |
| lifetime | "a, b" |
| duration | "0ms, 300ms, 500ms" |
| timing-function | "ease-in" |
| delay | "0ms, 0ms" |
Ignoring the non-lifetime properties during the action is done as a performance optimization. Their values have no effect (since the original values were captured when the action started), so the work of restoring them is delayed until the animation completes so that the initial animation can get off the ground faster. It does however create the new rule that the application may not alter animation or transition properties while a WinJS animation is in progress.
When removing transitions, we remove the last matching item, so that we do not accidentally damage any values set by the application, because we always append our values.
For all actions, we listen on the completion event to know when that action has completed, and also create a watchdog timer to fire shortly after the expected completion of the action to handle the case where the action is abandoned without firing a completion. See Watchdog timeouts for further discussion.
All actions register their presence in the
activeActions array,
which is an array indexed by the combination of the
element's uniqueID and the
the name of the property being animated.
(Separated by a |
, because that character
is illegal in property names,
so it can safely be used as a separator.)
An action registers with a callback function to call to stop the action. This is called when a conflicting action is scheduled, so that the previous action can clean up and get out of the way before the new action starts modifying element properties. (Two actions are said to conflict if they attempt to animate the same property on the same element.) See Finishing due to interruption for further discussion.
If an array of actions or an array of elements is passed, then each combination of action+element gets its own listener and watchdog. All the corresponding Promises are joined together to become the final Promise of the combined action. Using the Promise.join method allows us to simplify our logic by allowing us to focus on the one action + one element scenario.
In Internet Explorer, layout is the act of applying the effects of all changes to the DOM that have taken place.
When you change the DOM, such as inserting or deleting an element or changing an element property, Internet Explorer flags the DOM as "dirty". When control returns to the Internet Explorer event loop, Internet Explorer checks if the DOM is dirty, and if so, it performs a layout pass, which takes all the changes and makes them take effect.
Forcing layout means forcing Internet Explorer to perform a layout pass outside of the event loop.
The PVL needs a layout pass to take place after the DOM has been edited by the application so that the objects move to their final positions, at which point it can accurately calculate the distance the elements have moved.
It turns out that Internet Explorer automatically forces layout if you try to read the offsetTop or offsetLeft properties when the layout is dirty. Therefore, we do not need to force layout explicitly; merely reading offsetTop and offsetLeft is enough to trigger layout.
In Internet Explorer, styles are resolved when the browser recognizes the value and takes action on it. Styles are resolved as part of the layout process, but you can also force styles to resolve outside of layout.
Since action is taken only when styles are resolved, if you change a property multiple times in a row without an intervening style resolution, then only the final value will have any effect.
The "intermediate writes are ignored" optimization does not cause a problem with most properties, but it affects transitions and animations because transitions and animations capture the values of properties at the point they are triggered, so we have to make sure the properties have the desired values at the point of triggering. If we update a property, then return without resolving styles, then it's possible that the property will change again before the next time styles are resolved (say, by another call to the PVL), resulting in the desired changes from the first animation not taking effect.
Note that the concept of style resolution is not part of the W3C CSS3 specification, which merely leaves to implementation free to decide what the requirements are for a change to a property to be counted as having taken place before, after, or simultaneous to another property change.
In Internet Explorer, resolving any style on an element resolves all styles for that element, so we can combine multiple style-resolve operations into one if all of the modified styles apply to the same element.
All event handlers are registered on the document rather than on the element itself, because an element whose disabled property is set to true does not receive events. Instead, the event bypasses the event and immediately bubbles to the parent.
This quirk is not documented anywhere that I can find.
Both transitions and animations use watchdog timeouts to catch abandonment scenarios. The watchdog timeout is set to the expected end of the action, plus a little extra time to ensure that when the action completes normally, the completion event fires ahead of the watchdog.
Scheduling the watchdog timeout is tricky
because setTimeout
starts the timer
immediately,
even though the animation timer doesn't start until
the next render pass.
(This is technically a violation of the CSS specification,
which says that the timer begins at the time the style
is changed.)
If we had scheduled the watchdog timer with a normal
setTimeout
,
then a long render pass
(which can happen for complex animations)
can result in the watchdog timer firing prematurely.
Therefore, we schedule the watchdog timer as a series of timeouts. The first timeout is for a fixed small amount of time. This guarantees that the first timeout will not expire until the first render pass is complete and the animation has started. At that point, we can schedule the real timeout, and this one will not fire prematurely.
Since we will sometimes cancel old actions while
setting up new ones,
we are careful to fulfill Promises asynchronously
by calling WinJS.Utilities.Scheduler.schedule(c)
rather than
c()
.
Otherwise, the application's completion handler might
try to schedule a third action while we are still
trying to set up the second one,
resulting in reentrancy and confusion.
The exception to this rule is in the case where the Promise is being canceled (see below). In that case, we complete the Promise synchronously. This will not create reentrancy because the cancellation was initiated by the application in the first place. Any cancellations performed by the Animation Engine are done via internal mechanisms and are still completed asynchronously.
When you create a Promise, you can optionally pass a cancellation callback function which is invoked when the application invokes the cancel method on the Promise. Upon cancellation, the Promise enters the error state, and the error handler is called.
Animations don't want to work that way. When the application invokes Promise.cancel, the animation should jump to its end state and complete immediately with success.
We have confirmed with the base team that it is supported and designed behavior that if a cancellation function calls the completion callback before returning, then the Promise will enter the fulfilled state rather than the error state. (This is why cancellations complete synchronously: So that we can complete the Promise during the cancellation callback and cause the Promise to complete with success rather than error.)
Therefore, whenever we create a Promise, we register a cancellation callback which cleans up the animation (thereby jumping to the end state) and synchronously invokes the cancellation function.
Styles on an element are stored in the element.style
object,
and calculated styles are obtained by calling
window.getComputedStyle
.
There are three ways of accessing styles from these objects.
Here they are with their relative cost,
where indexing is normalized to have a cost of 1.
| Technique | x = element.style | x = getComputedStyle(element) |
| x.getAttribute(propName) | 10.82 | 6.97 |
| x[propName] | 1.00 | 1.00 |
| x.opacity | 0.95 | 1.00 |
One theory is that
calling getAttribute
is slow because we end up making two calls into the DOM.
First, we have to ask the DOM for the function pointer
for the getAttribute
method.
Then we have to take that raw function pointer and convert
it to a JavaScript function object.
Those two steps together are already 2½ to 3× slower than
indexing.
Next, we have to call the JavaScript function object
with the two parameters,
which means unwrapping the object back into the raw
function pointer.
That last step takes 4½ to 7× as long as indexing.
At any rate, it's clear that indexing is the way to access attributes from style objects. Switching from getAttribute/setAttribute to indexing improved time-to-first-frame by 5%.
If you're going to be manipulating styles a lot,
you should cache the style
object in a local
variable to avoid having to hit the DOM each time you type
element.style
.
The translation portion of a CSS transform matrix
is in positions e
and f
in the
"matrix(a, b, c, d, e, f)"
value.
(If there is no transform, then the string
returned for transform is "none"
.)
The algorithm for extracting the e
and f
from the matrix string splits
the string on commas, and if six fields are produced,
it extracts fields 4 and 5 (counting from zero).
There will not be six fields if the string is "none"
,
but in that case, the effective transform is the identity,
so there is no translation.
Note that we take advantage of the fact that parseFloat
ignores trailing garbage,
because field 5 will have a trailing close-parenthesis.
You might think that a more efficient algorithm would be
var matrix = new MSCSSMatrix(window.getComputedStyle(e).transform); left += matrix.e; top += matrix.f;
It turns out that it's three times faster to do the parsing entirely in native JavaScript (which can be optimized) rather than calling into the DOM four times (once to construct the MSCSSMatrix, once to retrieve e, once to retrieve f, and once to destruct the matrix).
Multiple styles can be updated at once by appending to the
cssText
property:
element.style.opacity = 1; element.style.backgroundColor = "red"; // versus element.style.cssText += "; opacity: 1; background-color: red";
The leading semicolon is important so that the new CSS attributes are properly separated from the previous ones.
Although this would appear to be an improvement since it reduces the number of round-trips to the DOM, no measureable improvement was observed in practice because of the cost of generating the original value of cssText. The benefit appears to be dependent on the complexity of the existing styles on the object.
At any rate, we cannot use this technique even if it did provide an improvement because the Internet Explorer implementation of cssText does not properly return properties whose values contain an at-sign, semicolon, or quotation-mark. (The at-sign is used in font names to indicate that the font is vertically-oriented.) This bug in Internet Explorer is marked Won't Fix. Since we allow animations to be triggered on arbitrary application-provided elements, we cannot guarantee that the application's styles avoid the dangerous characters.
As noted in the opening discussion, shorthand properties are not reliable for reading, but they work just fine for writing. (Though there are problems if a name contains an escaped reserved character, but those problems existed even in the lifetime-property itself, so we're not introducing any new issues here.)
Using shorthand properties for writing saves a number of round trips into the DOM.
DOM events do not propagate to disabled elements. The AppBar control will sometimes animate disabled elements (for example, disabled buttons will animate into view).
The workaround is to register the listener on the document root, which remains enabled. However, doing this unconditionally is expensive because it generates a lot of listener traffic which we end up just ignoring. Therefore, we move the registration to the document root only if the element is disabled.
If the element is enabled at the time the action begins, but becomes disabled during the course of the action, then the DOM completion event will not be delivered to the listener. The watchdog timer catches this case and ensurse that the Promise completes eventually.
It turns out that DOM events are expensive. Therefore, we avoid them whenever possible.
We use the activeActions array to keep track of all the actions that we have scheduled which are still active. When an actions begins, it registers with the array, and when it completes, it unregisters. This is a native JavaScript object and therefore can be manipulated without going through the DOM.
As part of setting up an action, we check if there is already an active action on the same element and property. If so, we finish the old action before replacing its registration with the new action.
By having our own table of active actions, we don't need to listen to the transitionstart event to detect stoppage due to interruption: We stop the action explicitly when the interrupting action is scheduled, rather than relying on the DOM event to notify us. (It also is important in the case of an interrupted Animation, because an interrupted animation does not fire an animationend event at all.)
It also allows us to stop actions on disabled elements. As noted above, native DOM events do not propagate to disabled elements.
To reduces round trips to the DOM, we retain a private cache of what we think the DOM styles are and use that cache instead of accessing the DOM directly. The cache is obviously write-through, because we want our changes to take effect, but it avoids DOM accesses on reads.
None of these optimizations led to measurable improvements.
(a ? a : "") + b
to
(a ? (b ? a + b : a) : b)
which avoids the concatenation if a is null or or b is ""
.
These failed optimizations can be explained by the first-order approximation that the cost of the PVL is dominated by calls into the DOM, and none of the above changes affected the number of calls into the DOM.
The responsibility for managing transitions is broken into two portions. The high-level animations.js file (the animation library) deals with calculating the necessary transforms, and the low-level transitionAnimation.js file (the animation engine) deals with actually applying them.
The first part is an interaction between the application and the animation library.
The second part is handled by the low-level animation engine.
Let's walk through the above steps with an example. consider an object that starts out at position left = 100.
The remainder of this section discusses the details of the algorithm.
The position of an element may be animating at the time the transition is applied. We must capture the in-flight value of the position.
The current layout position can be obtained from the offsetLeft and offsetTop properties.
The tricky part is that we also want to incorporate any translation transform that may be active on the element. To do this, we use window.getComputedStyle(element).transform and extract the x- and y- coordinates from the transformation matrix. Note that the in-flight position is relevant only when capturing the original positions. Final positions need to capture only the layout position, since we are going to be changing the transform ourselves.
It is not well-known that the browser does not respond to a change in a CSS value until the style is resolved. (See Resolving styles above.) As a result, many developers will write code like this
// incoming's initial opacity is 1. // Set it to 0 so it will fade in. incoming.style.opacity = 0; WinJS.Animations.crossFade(incoming, outgoing);
Unless special action is taken, this code will not actually animate from opacity 0 to 1, because the starting opacity of 0 was never resolved before the animation engine changed it to 1. As a result, the browser will see that the opacity changed from 1 to 1 and perform no animation.
To cover for this type of mistake, the animation library will automatically resolve the initial CSS property values so that the above code will work.
Since this workaround already is present, we may as well take advantage of it in the animation library: After applying the counteracting transform to each element that moved, we would normally resolve the style so that the initial transform is not animated, and so that it will be taken as the starting point of the animation. But we can skip that step and let the animation engine do the resolve.
When the animation library applies the initial counteracting transform, it does so without animation because our overall design removes the transition properties as soon as the transition begins. Therefore, there are no leftover transition properties that would cause the initial transform to be animated.
After setting the initial transforms, we resolve the transform style so that the browser observes the initial value before we start enabling transitions.
Transitions are triggered by changes to element properties. If the target element's property has a value equal to our desired transition target, then no transition takes place, and consequently, transition events do not fire. We detect this case and convert the transition to a simple timeout.
Note that Internet Explorer returns values that may be different from the value used to set the property. Examples:
Internet Explorer is inconsistent as to whether changes to equivalent values are considered changes for the purpose of triggering transitions. In the above examples, changing the transform to an equivalent translation does trigger a transition, whereas the change the equivalent opacity does not trigger a transition.
To avoid surprises, we ask Internet Explorer to munge the value so we can see how it looks after it has been internalized and spit back out. To do this, we create a temporary element (never added to the document, so it does not trigger layout) and set the property on the temporary element, then read it back, and use that result as the "canonical form" for the property value. (If for some reason, Internet Explorer is inconsistent with its canonical forms, the watchdog timeout will save us.)
If the animating property is modified, either by the application directly, or because the application requested a conflicting transition, no transitionend event is fired. From the browser's point of view, the transition is still in progress; it merely got redirected. The transitionend event fires only when the new transition completes.
For the case where a conflicting action is requested by the application, we manually fire the cleanup callback so that the previous transition will clean up and get out of the way.
Note that if an application attempts to apply a transition to an element without using the animation engine, the animation engine will not detect the interruption. We do not consider this a primary scenario, since applications should be using the animation library for all animations. This will instead be treated as an Abandonment scenario.
For the purpose of this document, we shall use the term abandonment to refer to situations in which the final transitionend event does not fire, and the interruption was not detected when the animation engine scheduled a conflicting action.
As we noted above, if a new value is set for the property undergoing transition, the existing transition is considered to have been redirected.
According to the CSS3 Transition spec, section 5:
In the case where a transition is removed before completion, such as if the transition-property is removed, then the [transitionend] event will not fire.
The example given refers to this scenario:
The act of setting -ms-transition-property to "opacity" causes the transform transition to become abandoned, since "transform" is no longer set.
We are careful never to remove properties from the -ms-transition-property that we did not put there ourselves. If there are multiple transitions on the same property, the property name appears twice in the list, so that removal of the old transition will still leave the property name in the list and not result in abandonment of the new transition. The only time this problem will occur is the application removed the transition.
One way of detecting abandonment is to register a listener on all attribute changes, and detect that somebody has deleted "transform" from the transition-property attribute. We will not do this, however, because the problem is fixed by the more general solution below.
Another case where transitions can become abandoned is if the element is removed from the document. There is a W3C DOM event DOMNodeRemovedFromDocument which fires when this occurs, but Internet Explorer does not support this event.
We handle transition abandonment generally by scheduling a timeout on the element which fires slightly after the anticipated completion of the transition. The timeout function completes the transition promise if it has not yet completed.
The timeout is canceled as part of completion.
See Watchdog timeouts for further discussion.
@-ms-keyframes uniquename { from { property: initialValue; } to { property: finalValue; } }
Animations behave better with respect to interruptions than transitions. If an animation is interrupted, its animationend event still fires.
Animations can become abandoned if the keyframe is deleted from the -ms-animation-name property by the application, or if the element is destroyed. In these cases case, no animationend event is fired.
We protect against this the same way we protected against abandoned transitions: By creating a watchdog timeout that fires if the animationend event did not fire as expected. When the watchdog timeout fires, we assume that the animation was abandoned and perform our cleanup. See Watchdog timeouts for further discussion.
During the delay before the start of the animation, and after the animation completes, the element's properties are controlled by their actual styles rather than the keyframe. This behavior can be altered by the standard-proposed animation-fill-mode property.
| Fill mode | Description |
| none (default) | properties are not affected outside the animation timeline |
| backwards | after the animation is applied and before the animation starts, the properties have the value of the keyframe's initial value |
| forwards | after the animation ends and until the animation is removed, the properties have the value of the keyframe's final value |
| both | combine backwards and forwards |
The animation-fill-mode is a comma-separated list of values, each one applying respectively to the corresponding comma-separated keyframe name. Like the other animation properties, it is captured when the animation begins.
This property permits us to let the animation "hold" the initial and final values outside of the keyframe.
Note, however, that the effect of the animation-fill-mode extends only as long as the animation remains applied to the element. This creates a problem for staggered animations, because each individual participant in the animation will complete at a different time, but the promise returned by the animation library does not fire until all participating elements have finished animating. Each individual participant does not know when the other participants will complete, so it will not know how long to delay clearing the keyframe. This gets particularly complicated when multiple animations are combined, since there is no way for a Promise to know that it has been joined or chained to another Promise.
Solving this problem would require applications who compose animations (via chaining or joining) to communicate the "final completion" back into the animation engine so it can perform final cleanup. This is problematic, so we solve the problem by punting: If the application wants the object to retain its final property, it needs to set that property prior to triggering the animation.
e.style.opacity = 0; // NEW WinJS.UI.Animation.fadeOut(e).then(function() { e.parentNode.removeChild(e); });
Although cumbersome, this approach does have the following advantages:
We may want to change fadeIn and friends from animations to transitions, so that the final opacity sticks at the end rather than reverting. This does introduce a breaking change, but perhaps the scope of the break is not significant, since people are likely to expect the opacity to be updated anyway.
The enableAnimations and disableAnimations methods allow an application to influence whether the PVL performs animations. The isAnimationEnabled method allows an application to query whether the PVL will perform an animation. This allows an application to enable or disable its manual animations in sync with the PVL.
The animation enableCount starts at zero.
Calling enableAnimations increments the enableCount, and calling disableAnimations decrements it.
The PVL uses this algorithm to determine whether animations are performed:
This model permits the following usage patterns:
doStuff();
enableAnimations(); alwaysAnimates(); disableAnimations();
disableAnimations(); neverAnimates(); enableAnimations();
Useful helper functions would be
function withoutAnimation(f) { try { WinJS.UI.disableAnimations(); return f(); } finally { WinJS.UI.enableAnimations(); } } function forceAnimation(f) { try { WinJS.UI.enableAnimations(); return f(); } finally { WinJS.UI.disableAnimations(); } } withoutAnimation(function() { neverAnimates(); }); forceAnimation(function() { alwaysAnimates(); });
To give Internet Explorer more time to set up animations and transitions, an artificial delay of 34ms (two frames) is added to all actions scheduled by the animation engine. Clients can opt out of the artificial delay by setting exactTiming: true in their action descriptor.
The 34ms delay was originally added in changelist 580919 for Windows 8 bug 900769. Windows 8 had a library delay of 34ms (two frames). Improvements in IE in Windows 8.1 removed the need for this delay, and the default delay is now zero.
Components which trigger CSS animations and transitions manually
can retrieve the the animation engine delay from the
WinJS.UI._libraryDelay
property.
This returns the delay in milliseconds.
The
WinJS.UI._libraryDelay
is writeable,
and changes take effect for all future animations and transitions.
(Animations/transitions in progress are not affected.)
Any member of the Animation or Transition object can be set to a function rather than a fixed value. In that case, the function is called for each element, and its return value is used as the corresponding value of the Animation or Transition for that element. Using a function allows a caller to provide a different action for each element. For example, this technique is used to implement stagger animations, and to allow animating objects to move to different final positions.
If you provide a function, it is called with the following parameters:
| Parameter | Type | Description |
| i | number | zero-based index of the animating element's group |
| e | DOM element | the element being animated |
For example, if the target elements for an animation or transition are
passed as [a, [b, c], d]
,
then the callback function will be called four times:
f(0, a);
f(1, b);
f(1, c);
f(2, d);
The first parameter to the callback is 1 for both b and c since they belong to the same animating group.
An Animation object describes the properties of an animation.
| Member | Type | Description |
| keyframe | string (optional) | the predefined keyframe to use for this animation. |
| property | string | the property to animate. "transform" and "opacity" are the only ones used by the PVL. |
| delay | number | delay before the animation begins, in milliseconds |
| duration | number | animation duration, in milliseconds |
| timing | string | CSS timing function |
| from | value (optional if keyframe is provided) | starting value for property specified by property |
| to | value (optional if keyframe is provided) | ending value for property specified by property |
| exactTiming | boolean (optional) | set to non-false to disable time-to-first-frame compensation. See Compensating for time-to-first-frame issues in Internet Explorer. |
All members are required except where noted.
If required, the type of the from and to members must be compatible with the property specified by the property member. In other words, if property is "transform", then from and to must be strings which are valid transforms. If property is "opacity", then from and to must be numbers between 0 to 1.
(Note that the empty string is not a valid transform. To animate to a null transform, you must specify an explicit null transform: "none".)
If a keyframe is provided, then the animation will be performed according to the sequence described by the keyframe. In that case, the from and to in the Animation object are ignored. Note, however, that you must still pass a property which matches the keyframe, so that action conflicts can be detected and dealt with.
Any member of the Animation object can be set to a function that returns the desired value. See Dynamic action properties for more information.
The animation engine creates copies of the Animation object for capture purposes, so that changes made by the calling application to the Animation object do not interfere with our callbacks, and so that we can attach private information to the copy. (Earlier versions of the animation engine modified the Animation object, which caused problems if the application re-used the Animation object for multiple animations.)
After copying the Animation object (evaluating functions as necessary for dynamically properties), we add the following internal properties to our copy:
| Member | Type | Description |
| keyframe | string | the auto-generated unique keyframe name |
A Transition object describes the properties of a transition.
| Member | Type | Description |
| property | string | the property to animate. "transform" and "opacity" are the only ones used by the PVL. |
| delay | number | delay before the animation begins, in milliseconds |
| duration | number | transition duration, in milliseconds |
| timing | string | CSS timing function |
| from | value (optional) | starting value for property specified by property. If not specified, then property begins animating from its current value. |
| to | value | target value for property specified by property |
| exactTiming | boolean (optional) | set to non-false to disable time-to-first-frame compensation. See Compensating for time-to-first-frame issues in Internet Explorer. |
| skipStylesReset | boolean (optional) | set to non-false to disable setting the transition style to empty when the transition ends. This can improve performance of scenario where the element will be removed from the DOM when the transition ends. |
All members are required except where noted.
The type of the to member must be compatible with the property specified by property. In other words, if property is "transform", then to must be a string. If property is "opacity", then to must be a number between 0 and 1.
(Note that unlike animations, for Transitions, you pass the empty string "" as the to value if the property is "transform".)
As with Animation objects, the animation engine creates copies of the Transition object for capture purposes as part of the evaluation. Otherwise, if the application modified the property member after the call to executeTransition returned, our event handlers would be looking at the wrong property.
An action descriptor object provides the traits of an action.
| Member | Type | Description | ||||||||||||
| shorthandProp | string | CSS name of the shorthand property that sets all the action properties at once | ||||||||||||
| nameProp | string | CSS name of property that controls action lifetime | ||||||||||||
| nameField | string | name of member of the Action/Transition object to set into the lifetime property | ||||||||||||
| props | array | an array of arrays, each one describing one CSS property
and how it should be initialized
from the Transition or Animation object.
The properties are listed in the order they appear in the
shorthand property.
|
||||||||||||
| isTransition | bool | true if this is the descriptor for a transition.
Transitions have a few extra quirks that animations don't,
like having to set the lifetime property to "none"
to stop all transitions.
|
||||||||||||
| styleCaches | hash | the active style cache objects, indexed by uniqueID |
There is no good reason why the props is an array of arrays rather than an array of records. It just ended up that way for historical reasons, and there was no justification for changing it. (One might retroactively justify it by claiming that integer indexing is faster than hash indexing, but the difference is not measureable.)
A style cache object is a JavaScript object with the following properties:
| Member | Type | Description |
| cref | number | reference count, the number of actions
which are using this object.
Each action calls removeName when it completes. |
| id | string | uniqueID of the element whose styles are being cached |
| desc | object | the action descriptor that created the style cache object |
| removed | hash | lifetime object names we have removed since the last style resolution. This is used to detect that we need to force a style resolution if the same name is added back. |
| prevStyles | array | the original styles on the object, to be restored when all actions complete. This array runs parallel to desc.props. |
| prevNames | array | the original value of the desc.nameProp style. |
| names | string | the current cached value of the element's desc.nameProp style |
Note that we do not cache a reference to the HTML element or any other DOM objects. The style cache object refers only to strings and global objects. That way, if there is a leak (for example, due to an exception), we only leak some JavaScript strings and objects and not HTML elements (or worse, HTML element trees).
When the reference count drops to zero, the original styles are restored from the prevStyles and prevNames members.
The underlying problem is that changes to the lifetime property are detected only at resolve time. This causes problems for animations, because an animation often interrupts another instance of itself. When that happens, we want the animation to restart, which means that we need to delete the animation from the animation-name property, resolve the style (so that the browser sees the deletion), then add it back.
To detect the case where we need to force a resolution before setting the new value, we see if the animation being added is one that was recently removed. If so, then we force a resolution to ensure that the deletion is seen by the browser.
The removed hash can be cleared if we explicitly resolve styles. But in the common case, the styles are resolved implicitly by the event loop, so we do not know exactly when it has occurred. It is not worth scheduling a setImmediate to clear the hash, because the cost of calling into the DOM to manage the callback far exceeds the savings of this technique. (Two DOM calls, one to cancel the old immediate and another to schedule a new one.)
In practice, objects are not continually kept in an animating state, because PVL animations do not support autorepeat; they are all one-shot animations. Eventually, objects stop animating, and then the entire style cache object goes away.
Note that if an application starts a new animation on completion of an old animation, we do have a brief moment where there are no active animations, so that case is not considered a "continual animation." To get a continual animation, the application would have to keep interrupting the animation with a new animation and never let the old animation end naturally.
Create a new StyleCache object associated with a particular element by capturing the element's initial styles (so they can be restored later).
| Parameter | Type | Description |
| id | string | uniqueID of the element whose styles are being cached |
| desc | object | the action descriptor that created the style cache object |
| style | StyleCSSProperties | style object of the target element |
| Returns | Description |
| StyleCache object | The newly-constructed StyleCache object |
Destroys a StyleCache object and restores the original styles to the element being tracked.
| Parameter | Type | Description |
| style | StyleCSSProperties | style object of the target element |
| Returns | Description |
| none |
Destroying a StyleCache object entails the following steps:
Removes a reference to an action name from the associated action property. If other actions remain, then the name is removed from the list. If no other actions remain, then all styles are restored to the element, and the style cache is destroyed.
| Parameter | Type | Description |
| style | StyleCSSProperties | style object of the target element |
| name | string | name to remove from comma-separated list |
| elem | DOM element (optional) | underlying element if this is the cancellation of a transition |
| Returns | Description |
| none |
Suppose the element's
elem.style.lifetime
property has the value
"a, b, c, d".
Calling
removeName(styleCache, elem.style, "b")
would result in the property changing to
"a, c, d".
The "b" was removed from the list.
The lifetime property is inferred from the action descriptor associated with the style cache object.
Remember that Internet Explorer actually separates items with a comma and space, so we need to include the space in our parsing algorithm.
Removing a name decrements the style cache object's reference count, since there is one fewer outstanding action.
Remember the name as one which was recently removed, so that we will know to force a style resolution if it is added back. This does not need to be done for our auto-generated names, because those are guaranteed unique and will never be seen again. This avoids unbounded build-up of names in the case where the application manages to keep the element animating continuously.
As a special optimization, if the style cache object is about to be destroyed, then there is no point deleting the name from the lifetime property since we're going to restore the lifetime property as part of the style restoration that occurs during destruction.
Observe that when we update the lifetime property, we also update the styleCache.names so that our cache remains valid.
The nameValue local variable and the elem parameter are to handle a strange edge case with canceled transitions. For reasons known only to the W3C, the default value for the transition property is "all". This means that setting the value to "" is not the same as disabling all transitions; in fact, it enables all transitions!
Therefore, if we are trying to cancel the last transition, we have to set the transition property to the special value "none", resolve the style (which forces the transition to stop), and then set it to the empty string.
This extra step is not required for normal expiration of transitions, because there is no need to force the transition to stop; it already stopped on its own.
The caller passes a non-null elem if it is calling to cancel a transition. We then perform the extra step if we find that we just removed the last transition.
Note that it is possible for the last name to be removed from
the this.names but still have a nonzero reference count.
That happens if the last name was removed as part of canceling an
old action while we set up a new one.
In that case, we do not want to force any style resolution to
"none"
because we're going to be doing a style resolution
in
setTemporaryStyles anyway.
This avoids a wasteful double-resolve.
Takes an object and converts it into something that supports indexing and has a length property.
| Parameter | Type | Description |
| elements | object (optional) | collection or single element |
| Returns | Description |
| object | an object which supports indexing and has a length property |
If a single object is passed, then we turn it into a one-element array.
If undefined is passed, then we turn it into a zero-element array.
Returns a unique string to use as a keyframe name.
| Parameter | Type | Description |
| none |
| Returns | Description |
| string | a unique string to use as a keyframe name |
JavaScript integers overflow at 252. Even if running a million animations per second, overflow will not occur for over 140 years. I like those odds.
Determines whether the specified name was generated by getUniqueKeyframeName.
| Parameter | Type | Description |
| s | string | the name to check |
| Returns | Description |
| boolean | true if the string is a name generated by getUniqueKeyframeName. |
Creates a callback function which creates a shallow copy of a JavaScript object. If any member is a function, then the function is called to resolve it to a scalar. This function is used to capture Animation and Transition objects.
| Parameter | Type | Description |
| iElem | zero-based index of the animating element's group | See below |
| elem | DOM element | the element being animated |
| Returns | Description |
| function | function which creates a shallow copy with function evaluation |
The return value is a function suitable for use as a callback for Array.map. The function creates a shallow copy of the object and returns the copy. If any member of the source object is a function, then the function is called with the iElem and elem parameters.
Stops any active animation on an element for a particular property. This is done by calling the registered cleanup function, if any, specifying that the cleanup is due to interruption. The finish function is expected to call unregisterAction as part of its cleanup.
| Parameter | Type | Description |
| id | string | uniqueID of the element |
| prop | string | CSS name of property whose animation is to be canceled |
| Returns | Description |
| none |
Registers a function to be called if an action needs to be canceled.
| Parameter | Type | Description |
| id | string | uniqueID of the element |
| prop | string | CSS name of property being animated |
| finish | function (reason) | function to call to clean up the animation. |
| Returns | Description |
| none |
Callers are expected to have called stopExistingAnimation at some earlier point.
The finish function is called as follows:
| Parameter | Type | Description | ||||||||||||
| reason | integer (optional) | reason for cleanup.
|
| Returns | Description |
| none |
Unregisters the function registered by a previous call to registerAction.
| Parameter | Type | Description |
| id | string | uniqueID of the element |
| prop | string | CSS name of property whose animation or transition has completed |
| Returns | Description |
| none |
Changes the styles of an element to add new actions, creating a style cache object if this is the first action to be applied to the element.
| Parameter | Type | Description |
| elem | HTMLElement | the target element |
| style | StyleCSSProperties | style object of the target element |
| actions | array | array of Transition or Animation objects |
| desc | action descriptor | traits of the action to be applied |
| Returns | Description |
| function () | function to call to clean up temporary changes |
The function proceeds in the following steps:
removeName
calls has been made.
Note that it is important that we do this before
canceling the existing actions, so that the style cache object
will not be destroyed if we happen to cancel the last action.
This allows us to interrupt an animation without wasteful
style resolutions.
If we did not force the styles to be resolved, then they will be resolved naturally when control returns to the event loop.
Apply an array of transitions to a single element.
| Parameter | Type | Description |
| elem | DOM element | target element |
| index | number | zero-based index of the animating element's group |
| transitions | array of Transition objects | transitions to apply to the element |
| promises | array of Promise objects | any Promises created by the function are appended to this array |
| animate | boolean | true if animations should be performed |
| Returns | Description |
| none |
As we saw in the introduction, the property mapping for transitions is as follows:
| Characteristic | Property | Notes |
| Lifetime | -ms-transition-property | |
| Initial delay | -ms-transition-delay | append "ms" since the value is in milliseconds |
| Duration | -ms-transition-duration | append "ms" since the value is in milliseconds |
| Curve | -ms-transition-timing-function |
We create private copies of the Transition objects so that we are insulated from changes made by the application, and so that we can evaluate any callback functions.
If the transition has an explicit from, then set it.
Subtlety: We filter the to through the uniformizeStyle element to account for equivalent properties. See Edge case for transitions for further discussion.
For each transition, we hook up these callback functions:
| Callback | Fired by | Description |
| finish | setTimeout | Unregisters all callbacks and completes the promise. This function follows the rules for finish functions as described in registerAction. |
| onTransitionEnd | transitionend | If the transition being ended is for the property we are animating, then our transition ran to completion, and we call the finish callback to complete our promise and clean up. |
Note that event handlers are registered on the document if the element is disabled, for reasons described in Event handler gotchas.
If the element is already at the final value, then there is no transition, and we merely schedule the cleanup function after an appropriate timeout. We could have detected this case earlier and avoided all of the event handler set-up, but we'll do it this way to reduce churn during Windows 8 RTM. (The case of a null transition is not expected to be common, anyway.)
If the element is not at the final value, then we update the value of the animating property to trigger the transition. We let the style resolution happen when we return to the event loop.
The timeout associated with the finish callback is set in two steps to ensure that the time-to-first-frame does not count toward the timeout. We set the watchdog timeout to the expected completion time after the first frame has completed.
Note that the loop over the transitions must be performed via forEach rather than a for loop, because we need each iteration to capture a different transition local variable.
If animations are disabled (animate is false), then all we do is set the final values of the transitions into the element. No promise is generated since the operation completed synchronously.
When the finish function is called due to
reason_interrupted or reason_canceled,
we must pass the elem to
styleCache.removeName
because we may need to set
the transition-name to "none"
to force
the transition to stop.
When it is called due to
reason_canceled,
we pass true
to completePromise
for reasons explained in
Cancellation.
| Parameter | Type | Description |
| elem | see applyAction | target elements |
| transitions | array of Transition objects | transitions to apply to the elements |
| Returns | Description |
| Promise | The promise completes when all transitions complete |
The executeTransition function uses the applyAction function to do the heavy lifting.
Apply an array of animations to a single element.
| Parameter | Type | Description |
| elem | DOM element | target element |
| index | number | zero-based index of the animating element's group |
| anims | array of Animation objects | animations to apply to the element. Assumes that calcAnimationProperties has already been called to reinitialize each Animation object. |
| promises | array of Promise objects | any Promises created by the function are appended to this array |
| animate | boolean | true if animations should be performed |
| Returns | Description |
| none |
As we saw in the introduction, the property mapping for animations is as follows:
| Characteristic | Property | Notes |
| Lifetime | -ms-animation-name | |
| Initial delay | -ms-animation-delay | append "ms" since the value is in milliseconds |
| Duration | -ms-animation-duration | append "ms" since the value is in milliseconds |
| Curve | -ms-animation-timing-function | |
| Direction | -ms-animation-direction | hard-coded to "normal" |
| Iteration | -ms-animation-iteration-count | hard-coded to "1" |
| Persistence | -ms-animation-fill-mode | hard-coded to "both" |
We hook up the following callback functions for each animation:
| Callback | Fired by | Description |
| finish | setTimeout | Unregisters all callbacks and completes the promise. This function follows the rules for finish functions as described in registerAction. |
| onAnimationEnd | animationend | If the animation being ended is for our keyframe, then our animation ran to completion, and we call the finish callback to complete our promise and clean up. |
Note that event handlers are registered on the document if the element is disabled, for reasons described in Event handler gotchas.
The timeout associated with the finish callback is set in two steps to ensure that the time-to-first-frame does not count toward the timeout. We set the watchdog timeout to the expected completion time after the first frame has completed.
Note that the loop over the animations must be performed via forEach rather than a for loop, because we need each iteration to capture a different anim local variable.
If animations are disabled (animate is false), then we do nothing, since animations have no lasting effect. No promise is generated since the operation completed synchronously.
If an animation requires a custom keyframe, we create a temporary
STYLE
element to hold it.
The custom style sheet is removed after the animations have triggered.
In theory, this cleanup could be done via
setImmediate,
but setImmediate it turns out is not guaranteed to run after layout.
The current implementation of Internet Explorer runs timeouts after layout,
so we schedule a timeout and remove the element on the timeout.
(If the application emptied the document in the meantime,
parentElement will be null,
in which case
trying to disconnect from the nonexistent parent will raise a null
object exception, so watch out for that.)
In the future, we should switch to removing the style sheet when the last animation completes or is canceled. The original code removed the style sheet aggressively because of the documented 31-stylesheet limit in Internet Explorer. That limit was removed in IE10.
| Parameter | Type | Description |
| elem | see applyAction | target elements |
| anims | array of Animation objects | animations to apply to the elements |
| Returns | Description |
| Promise | The promise completes when all animations complete |
The executeAnimation function uses the applyAction function to do the heavy lifting.
This is either a
Windows.UI.ViewManagement.UISettings
object,
or a dummy object which reports that animations are always enabled.
| Property | Type | Description |
| animationsEnabled | boolean | true is animations are enabled in the system, or false if disabled |
If we can create the
Windows.UI.ViewManagement.UISettings object,
then we detect whether animations are enabled by
checking the
animationsEnabled property,
which corresponds to the
SPI_
system parameter.
If we are unable to create the object, then we substitute a dummy object whose hard-coded animationsEnabled property is true.
The try...catch
around the probe for WinRT
is to protect against problems that may occur if the Web page
happens to have a global object named Windows.
(Perfectly legal and not entirely unreasonable.)
If that's the case, we will take an exception when we try
to invoke WinRT methods on it,
since it's not actually WinRT.
If that happens, then we just say,
"Forget it, we don't have WinRT here."
We perform early tests against
window.Windows and
Windows.UI to avoid raising exceptions
in the common case,
thereby avoiding exception noise when the application developer
goes to debug their page.
(Otherwise, they will see this exception and think,
"Maybe that's why my page isn't working.")
| Parameter | Type | Description |
| elem | see below | target elements |
| action | array of Transition or Animation objects | target actions |
| execAction | function (elem, index, actions, promises) | callback to apply the actions to the element. Any Promises are appended to the promises array. |
| Returns | Description |
| Promise | The promise completes when all actions have completed. |
The elem parameter can be any of the following:
To elaborate on the array of arrays: Suppose the parameter is [a, [b, c], d]. This represents a transition involving four elements.
| Element | Stagger position |
| a | 0 |
| b | 1 |
| c | 1 |
| d | 2 |
In other words, elements b and c animate together.
We use the algorithm described in Globally enabling and disabling the PVL to determine whether animations are enabled. Note that we take advantage of the fact that boolean true has numeric value 1, whereas boolean false has numeric value 0.
After the execAction has been called on each animating element, we join together all the Promises, forming the final Promise for the action. If there are no promises to join (all operations completed synchronously), we return a zero-duration timeout promise so that the promise completes asynchronously. This preserves event-ordering behavior when animations are disabled.
Application use an offset object to provide animation offsets.
| Member | Type | Description |
| top | string: CSS [LENGTH] | the vertical displacement |
| left | string: CSS [LENGTH] | the horizontal displacement |
| rtlflip | boolean (optional) | if true, change direction of left if applied to an element whose direction is "rtl" |
| keyframe | string (optional) | if set, specifies the CSS keyframe to use for the animation. This keyframe must match the top and left parameters. If a keyframe is specified, rtlflip must not be true. |
The top and left are CSS [LENGTH] values, usually pixels. For example, "10px".
If the rtlflip property is true, and the offset is being applied to an element whose CSS direction property resolves to "rtl", then the sign of the left property is reversed.
The OffsetArray class captures the offsets provided by the application.
| Parameter | Type | Description |
| offset | object | offsets (see discussion below) |
| keyframe | string (optional) | keyframe name if default used (see discussion below) |
| defOffset | object (optional) | default offset to use if application did not provide one. If provided, this object must have top and left property. |
The OffsetArray constructor accepts an object in the following forms:
See the getOffset member function for a description of how these parameters are interpreted to determine the offset to apply to each element.
If the offset is empty or undefined, then the default offset defOffset is used.
If the offset is a single offset object or an array with one offset, we use the checkKeyframe method to see whether that offset happens to match the default offset for this animation. If so, we will use the keyframe. See checkKeyframe for further discussion.
See the chooseKeyframe property for a discussion of how we choose the keyframe in the case where the application asks for the default offsets.
The keyframe property specifies the predefined CSS animation keyframe to use, if any.
If the OffsetArray was constructed with an explicit application-provided offset, then there is no predefined keyframe.
If the OffsetArray was constructed with a default offset, then the chooseKeyframe function is used to determine what keyframe to return.
In all cases, the keyframe property is a value suitable for passing as the keyframe property of an Animation object.
Determines whether the application-provided offsets match the predefined keyframe offsets. If so, then proceeds as if the predefined offsets were used.
| Parameter | Type | Description |
| offset | offset | Application-provided offset |
| defOffset | offsets | Predefined offset |
| keyframe | string (optional) | keyframe name |
| Returns | Description |
| string | static keyframe to apply to all elements |
| function | function to calculate keyframe for each element |
| null | no keyframe applies to this offset |
If there is no keyframe provided, then the function returns null, indicating that there is no optimized keyframe for this collection of offsets.
If the application-provided offset does not match the default offset's top and left, then we cannot use the predefined keyframe. Return null.
If the application-provided offset flips in RTL, but we have only an LTR keyframe, then we cannot use the predefined keyframes. Return null. (We cannot return a function that sometimes returns a keyframe and sometimes returns null, because we need to know up front whether to pass a callback as the from and to members of the Animation or Transition object.)
If the application-provided offset does not flip in RTL, then we can apply the LTR keyframe statically to all elements.
Otherwise, the application-provided offset flips in RTL (and therefore the default offset also flips in RTL), so we can use keyframeCallback as our callback function.
Here's a table of the possibilities when a keyframe is available and the offsets match.
| offset.rtlflip | ||
| true | false | |
| defOffset.rtlflip = true | keyframeCallback | keyframe |
| defOffset.rtlflip = false | null |
Calculates the static keyframe name or keyframe callback function to use for a collection of animation offsets.
| Parameter | Type | Description |
| defOffset | offset | Default offset to apply |
| keyframe | string (optional) | keyframe name |
| Returns | Description |
| string | static keyframe to apply to all elements |
| function | function to calculate keyframe for each element |
| null | no keyframe applies to this offset |
If there is no keyframe provided, then the function returns null, indicating that there is no optimized keyframe for this collection of offsets.
If the defOffset defines a keyframe which does not need to flip in RTL, then the static keyframe is returned. This causes all animations to use the same keyframe.
If the defOffset defines a keyframe which
must flip depending on the element's direction,
then a function is returned which returns either the keyframe name
or the keyframe name with -rtl
appended,
depending on whether the element is LTR or RTL.
Callback function that decides whether to use the LTR or RTL keyframe for an element.
| Parameter | Type | Description |
| keyframe | string | keyframe name. The keyframe must be available in both LTR and RTL versions. |
| Returns | Description |
| function | function to calculate keyframe for each element |
The callback function returns the appropriate keyframe name based on the element's direction.
| Parameter | Type | Description |
| i | integer | Index of object from which to obtain offset |
| Returns | Description |
| object | an offset object |
If the OffsetArray was constructed with undefined
or an empty array,
then the i parameter is ignored, and
the function returns the defOffset passed to the constructor.
If no defOffset was passed to the constructor, then a
default offset of { top: "0px", left: "11px", rtlflip: true }
is returned.
If the OffsetArray was constructed with a single object, then the i parameter is ignored, and the single object passed to the constructor is returned.
Otherwise, the OffsetArray was constructed with an array.
The i'th element of the array is returned (zero-based index).
If fewer than i + 1
elements are present in the array,
then the last element in the array is returned.
(Effectively, the last element repeats indefinitely.)
Identical to makeArray in the animation engine.
| Parameter | Type | Description |
| elemArray | collection | target elements |
| Returns | Description |
| Array | Array of { top: value, left: value } objects
describing the current positions of the elements in the collection. |
The current position of the element is its current offsetTop and offsetLeft, combined with any active translation transform.
See Optimization notes: Parsing transitions for a discussion of the best way to parse the transform property.
Note that the returned array is not an array of offset objects. The top and left properties of the objects returned by collectOffsetArray are simple integers (representing pixels), not CSS lengths.
Creates the callback function for the delay member of the Animation and Transition objects used by the Animation Engine.
| Parameter | Type | Description |
| initialDelay | number | millisecond delay for first item |
| extraDelay | number | additional millisecond delay to be added for subsequent items |
| delayCap | number | maximum millisecond delay |
| Returns | Description |
| function (i) | Delay computation function which returns delay in milliseconds |
No animations in the animation library have a delay factor, and they all have a delay cap, so we can optimize the stagger function for linear stagger:
stagger = min(initialDelay + extraDelay × i, delayCap)
The nominal delay for item 1 is initialDelay + extraDelay * delayFactor
.
The nominal delay for item 2 is
initialDelay + extraDelay * delayFactor + extraDelay * delayFactor * delayFactor
.
In general, the nominal delay for item n is
nominalDelay |
= | initialDelay +
extraDelay ×
delayFactor i | |||
| = | initialDelay +
extraDelay × delayFactor ×
extraDelay ×
delayFactor i−1 | ||||
| = | initialDelay +
extraDelay × delayFactor ×
extraDelay ×
delayFactor i | ||||
| = | initialDelay +
extraDelay × delayFactor ×
|
If a delayCap is specified, then the calculated delay is
min(nominalDelay, delayCap)
.
Otherwise, the nominal delay is the calculated delay.
| Parameter | Type | Description |
| elemArray | collection | target elements |
| offsetArray | array | original positions |
| Returns | Description |
| none |
Updates offsetArray in place by subtracting the corresponding current locations of the elements in elemArray. Any active transforms on the elements are ignored for the purpose of determining the current location.
| Parameter | Type | Description |
| elemArray | collection | target elements |
| offsetArray | array | original positions |
| transition | array of Transition objects | transitions to apply to the elements |
| Returns | Description |
| Promise | The promise completes when all transitions complete |
A counteracting transform is applied to elements so that their visual position corresponds to offsetArray. The transform transition is then executed via executeTransition.
Creates the callback function for the to amd from members of the Animation and Transition objects used by the Animation Engine.
| Parameter | Type | Description |
| offsetArray | OffsetArray | array of offsets |
| prefix (optional) | string | optional string to prefix to the translation string |
| Returns | Description |
| function (i, e) | function which returns translation to apply to element number i, which is e |
The returned function takes a zero-based item number and calculates the translation for that item based on the offsetArray. If the offset is marked rtlflip and the target element's resolved direction style is "rtl", then the value is negated. (Note that our negation algorithm is purely textual, and it ends up negating "0pt" to "-0pt". Fortunately, "-0pt" is still a legal CSS dimension, and it will be simplified to "0pt" by the animation engine.
If the prefix is present, then it is prepended to the calculated translation.
Creates the callback function for the to amd from members of the Animation object used by the Animation Engine.
| Parameter | Type | Description |
| offsetArray | OffsetArray | array of offsets |
| suffix (optional) | string | optional string to suffix to the translation string |
| Returns | Description |
| function (i, e) | function which returns translation to apply to element number i, which is e |
The returned function takes a zero-based item number and calculates the translation for that item based on the offsetArray.
If the suffix is present, then it is appended to the calculated translation.
RTL flipping is not performed by this function. It is assumed that offsetArray is based on physical coordinates and therefore any flipping was performed during layout.
Callback function that decides whether to use a keyframe for an animation.
| Parameter | Type | Description |
| offsetArray | OffsetArray | array of offsets |
| keyframe | string | keyframe name |
| Returns | Description |
| function | function to calculate keyframe for each element |
The callback function returns the keyframe name if the corresponding offset is zero. Otherwise, it returns null and the Animation object's from and to values are used.


Stone Properties
Name: Gerald Fero
Last updated 1 week ago.
Name: Steve Kari
Last updated 1 week ago.
Name: Mandi Sato
Last updated 1 week ago.
Boove Homes
Name: Aggy Tana
Last updated 1 week ago.
Name: Gaga Javo
Last updated 1 week ago.
Name: Eric Doro
Last updated 1 week ago.
Emoticon Realtors
Name: Ton Wawi
Last updated 1 week ago.
Name: Mimo Sive
Last updated 1 week ago.
Name: Naomi Cotte
Last updated 1 week ago.
Morty Agencies
Name: Veida Obi
Last updated 1 week ago.
Name: Sivo Kato
Last updated 1 week ago.
Name: Bona Oma
Last updated 1 week ago.
Gaston Properties
Name: Angie Komi
Last updated 1 week ago.
Name: Merah Jana
Last updated 1 week ago.
Name: Tommy Vero
Last updated 1 week ago.


The Triangulate_Area macro takes a fault line and a series of boundary segments, determines which segments the fault line crosses and triangulates the fault line with the crossed segments. This macro is part of a suite of modules for advanced Triangle generation and manipulation.
Input Ports
Name Type Description in_fld Mesh Input data that represents the boundary segments. in_fault Mesh Input polyline that represents the fault line. Output Ports
Name Type Description out_mesh Triangle Mesh Output triangulated fault mesh. out_fld Field Original boundary segments with added cell data. out_obj DataObject Directly renderable output object.
The Triangulate_Area macro takes a fault line and a series of boundary segments, determines which segments the fault line crosses and triangulates the fault line with the crossed segments. Triangulate_Area is a higher-level macro that internally uses the triangle library to do the necessary calculations. The triangle library is a two-dimensional quality mesh generator and delaunay triangulator. It was written by Richard Shewchuk and more information about this library, its purpose and how it can be used can be found at http://www.cs.cmu.edu/~quake/triangle.html. Details of the license that this library is distributed under can be found in the
LICENSE
file. This suite of modules has been used in a large AVS/Express project for geologic modeling.The macro presented here combines the triangle and fault_region low-level modules to produce a macro that is able to determine which boundary segments a fault line crosses and then triangulate the fault line with the crossed boundary segments. This produces a triangle grid that can be used for further calculations and visualizations. It could, for instance, be used as an interpolation probe. The macro also generates a set of cell data and combines it with the input boundary segment data. The generated cell_data contains Null data for each segment that the fault line did cross. This allows the generated triangle mesh to be more easily seen. More information on the tasks that are being performed by this macro can be found on the triangle library home-page. An example of how to use the macro can be found in the TriFaultExample application.
in_fld
The input field that represents the boundary segments. The field should contain a polygon mesh.
in_fault
The input field that represents the fault line. The field should contain a polyline mesh. The polyline mesh should only contain a single fault line. If any more are present the macro will not work correctly.
out_mesh
The output triangulated field generated from the crossed boundary segments combined with the fault line. The output field contains a triangle mesh.
out_fld
The output boundary segment field generated by combining the new cell data with the original boundary segments field. The new cell data contains a 0 for each segment that was not crossed and a 1 for each segment that was crossed. The cell data is setup so that a 1 is treated as Null data. It does not contain any other data.
out_obj
The directly renderable output of the triangulate_area macro. This output contains both the triangulated mesh and the boundary segment field containing the new cell data. This output can be displayed in a 2D viewer. The modes and properties of each output object are set so that the various output objects can be identified.
Two utility macros are also provided along with the triangle library. These perform simpler tasks that are not done directly by the triangle library.
Triangle_Stats
Takes an input triangle mesh and calculates the area of each triangle. The calculated areas are placed into a cell data component. The macro outputs a new field structure and a renderable DataObject.
struct_to_fld
Converts a struct_triangulateio group into a field structure. The struct_triangulateio group is used by the low-level triangle module for both input and output. This macro takes that group and converts it into a triangle mesh and node data component that can be used by other Express module.
The Triangle project contains 8 example applications that provide a good illustration of what the triangle library is capable of. Most of these example use either the ClickSketch or ContinuousSketch modules to create input data. Those that use the ClickSketch module start out with a blank window and expect the user to create a polyline. This is done by clicking the right mouse button in the viewer window. You should see you polyline appear in red. Once you have completed this you should click the Done button. This begins the triangulation process. Once this has finished the results are shown in the viewer window. The ContinuousSketch module is similar except that the user should hold and drag with the right mouse button to create a trail of points.
The examples supplied are as follows, beginning with the ClickSketch examples.
ClickSketch Examples
TriConcavePolygonExample
Triangulates the specified Concave polygon.TriConvexHullExample
Determines the Convex Hull that surrounds the specified polygon.TriDelauneyTriangulationExample
Performs a Delauney triangulation of the specified polygon.TriPointInPolygonExample
Performs a Delauney triangulation of the specified polygon. If a point is picked using aCTRL
-click the triangle containing the picked point is determined and displayed.TriVoronoiExample
Performs a Voronoi triangulation of the specified points.ContinuousSketch Examples
TriFaultExample
Determines which cells are crossed by the specified fault line and triangulates the combined fault line and cell mesh.TriMovingRegionExample
Determines which cells are crossed by the specified fault region and triangulates the combined fault region and cell mesh.TriAnimatedRegionExample
Determines which cells are crossed by the specified fault region and triangulates the combined fault region and cell mesh. The loop module can be used to rotate the fault region. The output mesh is retriangulated after each move.
xp_comm_proj/triangle/trimacs.v contains the V definitions of the high-level macros Delauney, Voronoi, Convex_Hull, Concave_Tri, Triangulate_Area, Triangle_Stats and struct_to_fld.
The low-level TriangleMods library containing the low-level modules does not specify a process. By default the express process will be used.
Dr. David Knight, Advanced Visual Systems, Inc.
Ma YingLiang, Manchester Visualization Centre Andrew Dodd, International AVS Centre


If you join Accenture you can make great ideas happen for some of the world's most dynamic companies. With broad global resources and deep technical know-how, we collaborate with clients to cultivate ideas and deliver results. Choose a career at Accenture and enjoy an innovative environment where challenging and interesting work is part of daily life.
Accenture’s Client & Market career track drive profitable growth by developing market-relevant insights to increase market share or create new markets. They progress through required promotion into market-facing roles that have a direct impact on sales.
Accenture’s Client Delivery & Operations career track drive delivery and capability excellence through the design, development and/or delivery of a solution, service, capability or offering. They grow into delivery-focused roles, and can progress within their current role, laterally or upward.


A Javascript library adding distribution builders to your experiments.
In their paper 'Lay understanding of probability distributions' (2014), published in 2014, Daniel Goldstein and David Rothschild have highlighted the benefits of using graphical interfaces called distribution builders to study subjective probabilities, perceptions of frequency, and confidence judgements. This tool was first developed by Sharpe, Goldstein and Blythe (2000), and was featured later in Goldstein, Johnson and Sharpe (2008) and Delavande and Rhowedder (2008). However, the implementation of such distribution builders in online studies is not straightforward, as most survey platforms do not implement this type of question.
The distBuilder library was created to address this issue, and make the implementation of distribution builders easy and accessible to researchers. The library is user-friendly, and requires very little programming knowledge to be used.
You can cite distBuilder using its Digital Object Identifier (DOI).
This library is developed by Quentin André, an assistant professor of Marketing at the Leeds School of Business, University of Colorado Boulder. If you have any comment, feedback or suggestion regarding this library or its documentation, please let me know at<EMAIL_ADDRESS>or use GitHub issues.
To add the library to your projects and start using distBuilder, just follow the instructions below.
If you are hosting your experiment on a separate website, here is how to add the script:
Add the Javascript file distributionbuilder.min.js
and the CSS file distributionbuilder.css
to your html code:
<link rel="stylesheet" href="distributionbuilder.css">

$j
alias.
Instead of typing $('#Hello')
, type $j('#Hello')
.
If you prefer starting from a minimal working example and tweaking it, you can find a fully functional integration to Qualtrics here in the form of a .qsf file, that you can import in Qualtrics.
If you prefer detailed instructions, just follow these simple steps:
<link rel="stylesheet"<EMAIL_ADDRESS>


Main Info
All Types
Highlights
A powerful design kit with a huge set of customizable elements and templates
No spam. Only releases, updates and discounts


You can interact with the smart contract deployed on the Ethereum blockchain without using this web application. You can do so by configuring your own Ethereum wallet or by writing your own front-end.
The contract is deployed on the main Ethereum network at the following address: 0x67bFE8893d892B8B2ddE809A7FDA6caE934d0a9b.
It is also deployed on the following test networks:
Ropsten: 0x318914AE4ee285DBb7760d32242c816781AB2e97 Rinkeby: 0xd121f94184Da71908123a1e08F72cAB8573b9363 Kovan: 0x62dbf3f3ff221e383d7c696e706356cdb86fae56
The following is the contract ABI:


| Class | Grammar |
| In: |
lib/grammar.rb
|
| Parent: | Object |
This is the brains behind the whole operation. It gives FacebookBot the ability to speak and therefore interact with its friends and foes alike.
You create a Grammar instance as follows:
grammar = Grammar.new({:rulesfile => 'test.txt'})
It takes a hash of options as its parameter. Right now the only option available is the rulesfile. You can change the path to the rulesfile, but by default it is just ‘rules.txt.’ It looks for a file full of lines like this:
ADJECTIVE-->blue ADJECTIVE-->soft ADJECTIVE-->dumb VERB-->run VERB-->eat
You get the point. It creates a hash of @rules for later parsing. The hash has the capitalized words as its keys and Arrays of strings as its values. Example:
@rules = {'ADJECTIVE' => ['blue','soft','dumb'], 'VERB' => ['run','eat']}
You can do all sorts of nice things with this ruleset. The biggest and perhaps most important feature is its recursion. You can do things like this:
SENTENCE-->You're a ADJECTIVE-headed freak!
And it will auto-magically replace ADJECTIVE with a random adjective if you call the replace function. This is most extensively used in the Random class (which inherits this one), so maybe you should go peek your head over there.
Sets up a Grammar instance. Look at the docs (perhaps just look up!) for more info.
# File lib/grammar.rb, line 28 28: def initialize opts={} 29: @opts = {:rulesfile => 'rules.txt'}.merge(opts) 30: @rules = {} 31: 32: IO.foreach(@opts[:rulesfile]) do |rule| 33: next unless rule =~ /\S+/ 34: rule.gsub!(/\s+$/,''); 35: if rule =~ /^(.*)-->(.*)$/ 36: @rules[$1] = [] if @rules[$1].nil? 37: @rules[$1] << $2 38: end 39: end 40: end
Replace capitalized words with a random entry from @rules, if there is one. Check it out: it‘s recursive and simple and beautiful.
# File lib/grammar.rb, line 44 44: def replace line 45: line.gsub(/(\b[A-Z]{2,}\b)/) {|match| 46: @rules.has_key?(match<EMAIL_ADDRESS>: match 47: } 48: end


Collections of potentially useful tools for survey programming and related tasks on the Decipher by FocusVision platform
A. Visibility and a central repository for programmer-created utility scripts.
A. Any Decipher programmer.
A. Request membership or use a pull request.
A. Yes. Pitch your idea to an owner.
A. It's public, so be smart about what you put here.
A. According to the MIT License, the user. Still, carefully review anything you contribute.


|
Elektra
0.9.4
|
Elektra has three different main targets.
Elektra allows applications to read and write from a global configuration tree. We miss a specification (schema) so that these configuration values can be shared (integrated).
Known users:
Elektra is on the frontier for embedded systems because of its tiny core and the many possibilities with its plugins.
Known users:
Elektra is ideal suited for a local configuration storage by mounting existing configuration files into the global tree. Nodes using Elektra can be connected by already existing configuration management tools.
Known users:


$def with (org_name, ttls)
Org Retention lists the number of seconds that each type of data will be kept in storage and available.


The sequenceTester library is a Java library used for writing and testing unit tests for a possibly non-sequential APIs, i.e., were calls may block. The library has been used to test exercises in the undergraduate course "Concurrencia" at the Escuela Tecnica Superior de Ingenieros Informaticos at the Un iversidad Politecnica de Madrid, Spain.
The main idea is to write test cases as sequences of API calls, and to provide excellent diagnostics of why a test case failed, i.e., showing a complete trace of the calls of a failed test case, and indicating clearly the reason for failure. For example, that the call is blocked although it should have been unblocked by a call, or that it returned an incorrect value.


Need a perfect wildlife viewing getaway to the smallest Savannah national park in Uganda? Lake Mburo National Park reportedly shelters over 300 species of bird life, the Impala, Zebras, leopards plus a lot more wildlife that will spice up your adventure. This 3 days Lake Mburo safari will reward you a short but memorable visit to this national park.
Day 1 – Transfer to Lake Mburo National Park
You will leave Kampala very early in the morning and drive towards Lake Mburo through Masaka with a brief stopover at the equator for photography moments. Notably, the equator line separates the two hemispheres of earth and you will notice a lot of crazy stuff about climate at this place. Feel free to take lifetime photographs with feet on separate hemispheres, have a cup of coffee, relax and proceed to the pack for lunch and an afternoon boat ride that should reward you sights of hippopotamuses, various species of birds, buffaloes plus more animals. Overnight at Mihingo Lodge for luxury, Rwakobo Rock for moderate stay and Eagles Nest Camp for shoestring.
Day 2 – Morning Bush Walk and afternoon Horseback Ride
Right after having a nice breakfast meal, you will set off for a morning bush walk under the accompaniment of an armed game ranger to protect you from any harm. You should chance a lot of Zebras, the rare Impalas, Topis plus more wildlife before you return to the lodge for a lunch break. In the afternoon you do the same, only that this time round you go riding a horse as you watch wildlife. Whilst riding these horses, the mere fact that there is no engine sound will probably make you feel part of the nature and also get a chance to see more rare species of wildlife. Overnight at Mihingo Lodge for luxury, Rwakobo Rock for moderate stay and Eagles Nest Camp for shoestring.
Day 3 – Morning Game Drive and Transfer to Kampala
On this day, you will rise to as early morning breakfast, carry out a thrilling morning game drive and then get on your journey back to Kampala making another at the equator for coffee. When you reach Kampala, you will choose to either be dropped at the airport to catch up with your return flight / flight back home or rather dropped around your preferred accommodation facility for an overnight. If you wish to spend an extra night, we can arrange that for you as well.
You can also choose an activity to acompany your chosen package while in Uganda with us. While choosing an actvity, include it within your package details under the booking form to get a quotation.


Brüesch CRM
Christian Brüesch
Account Settings
Profile
Logout
Home
Companies
Contacts
Opportunities
Warning!
You need to have
JavaScript
enabled to use this site.
Home
Contacts
Contact
Wallstreet 32
Seond Street
98273 New York
United States of America
Phone: +1 (505) 12 34
<EMAIL_ADDRESS>Opportunities
Id
Name
Something Awesome
Something else Awesome
Actions
99
99
×
Settings
Here settings can be configured...
Admin templates
Bootstrap themes


Lorry driver blocks road after getting stuck in mud
He was wheel-spinning after ending up in a hedge
by Beth PriddingA farmer used his tractor to help a lorry driver out of trouble when he got stuck in the mud in a country lane.
Officers from Swadlincote Response Unit were called to the scene in Stanton-by-Bridge after the lorry got stuck and partially blocked the road.
The report came in at 9.55am this morning, Wednesday, November 20.
The lorry had ended up in a hedge at the side of the road near Swarkestone and, when trying to get out, got stuck in the mud.
A spokesman for Derbyshire Police said: "Swadlincote Response Unit went out to where it had happened on the A514, when a lorry had got stuck in the mud.
"The lorry driver was trying to get out of the mud, but it kept wheel-spinning and he was really struggling.
"A farmer was there when officers arrived and was helping to tow the lorry out of the mud. The road is now cleared."
We've got apps for both Android and Apple devices which can be tailored to deliver the news and sport that you're interested in.
And if you download our app, you can sign up for push notifications for Burton by selecting 'discover' and choosing 'Burton' from the 'more topics' list.


Analysts remain upbeat as Wilmar crushes 3Q results
by Samantha ChiewSINGAPORE (Nov 14): Analysts are upbeat and bullish on Wilmar International after the agribusiness group posted a 10% y-o-y increase in 3Q19 earnings to US$447.1 million (S$608 million).
3Q19 revenue came in 2.2% lower at US$11.2 billion from a year ago due to lower commodity prices.
Wilmar says the stronger bottomline was driven by better results in tropical oils and consumer products as well as the recognition of a gain from the disposal of the group’s discontinued operations in Brazil.
For the full story, visit www.theedgesingapore.com.


Consultation outcome
Ref: Ofqual/16/5925 PDF , 232KB , 5 pages
If you use assistive technology (such as a screen reader) and need a version of this document in a more accessible format, please email<EMAIL_ADDRESS>. Please tell us what format you need. It will help us if you say what assistive technology you use.
Because the use of calculators in exams affects the validity of exams, we have decided we will introduce rules governing the use of calculators in new GCSEs, AS and A levels.
We have made significant changes to our proposals to reflect consultation feedback:
We have also made a number of changes to simplify the drafting of the Condition, and to ensure it focuses on the outcome exam boards must achieve – assessments that are fit for purpose – rather than the process they might use to achieve that outcome. This intentionally gives exam boards flexibility to adopt different approaches to calculator use that reflect both subject-specific risks of calculator use, and their chosen approach to assessment.
We have also decided to confirm our proposal to withdraw the current subject-specific rules on calculator use in the new maths GCSEs, because they are no longer required.
Our decisions mean that:
Alongside this document, we have published revised versions of our:
Published: PDF , 449KB , 16 pages
If you use assistive technology (such as a screen reader) and need a version of this document in a more accessible format, please email<EMAIL_ADDRESS>. Please tell us what format you need. It will help us if you say what assistive technology you use.
There were 43 responses to the consultation – 26 from individuals, and 17 from organisations.
The majority of respondents supported our proposed approach, noting that it rightly places the onus on exam boards to ensure their qualifications are valid.
However, some respondents raised concerns about our proposals, in particular:
This consultation ran from
to
This consultation seeks views on how we should regulate the use of calculators in assessments for new GCSEs, AS and A levels.
Ref: Ofqual/15/5793 PDF , 638KB , 26 pages
This document sets out proposed new rules for the use of calculators in new GCSEs, AS and A levels.
We are proposing to put in place rules which require exam boards to ensure that – if students use calculators in exams – the assessments remain fit for purpose.
These rules would replace our published subject-specific rules for new GCSEs in mathematics, and also cover all other subjects at GCSE, AS and A level where students can use calculators in exams.
Published: 2 December 2015
Updated: 12 April 2016


Home
(current)
about
services
portfolio
pages
blog
contact
Recently Done Project
Quality Work
The jungle
Latest, Portfolio
Lathi
Popular, Portfolio
Madam Elita
Popular, Portfolio
Gatot Kaca is died
Upcoming, Portfolio
West Cowboy
Upcoming, Portfolio
Cyborg Alpa
Following, Portfolio
Knights of virtue
Following, Portfolio
A master thief
Following, Portfolio
Childhood humor
Upcoming, Portfolio


October 7th
October 7th
Lecture
- Finite State Machine:
- An abstract machine that can be in one of finite number of states.
- A set of states.
- Alphabet — A set of symbols.
- Transition Function: A set of rules (Can be given in a table) if in state X and looking at symbol alpha, then next stat is y. (Given a particular state and a particular symbol/imput, then transition to another particular state)
- Starting State: Need to know starting state to know where to go next.
- Set of accepting states: know whether a set of states is allowed or not (good/bad).
- Can be used in the development of a compiler.
- Seperate FSM for receiver and sender
- Regular Expressions: Way of decribing regular languages.
- Regular Languages: What finite state machines recognize.
- 4 Types of Languages
- Machine<==>Language
- Machine: Function/set of rules implied in hardwear. It is something that interprets language.
- Language: Defines a machine.
- Lexx: Unix program given adescription of a regular language, produces a C program (a finite state machine) that recognizes the language.
- 4 Kinds of codes:
- Conveince: Unicode/ASCII-->Assigns numbers to symbols and text.
- Data compression: Zip, JPEG, MP3-->Strips redundancy to send more data.
- Error detection/correction system: Checksum-->Adds redundancy.
- Encryption
Lab
No notes


This website uses local storage. By using this website you agree to our use of local storage.
Accept
Advanced Champion Mastery Viewer
JavaScript must be enabled to use this page.
BR
EUNE
EUW
JP
KR
LAN
LAS
NA
OCE
RU
TR
Show help
Filter
Ignore free
Free
Not free
Ignore chest
Chest granted
Chest not granted
Ignore custom list
In custom list
Not in custom list
Last played after
Last played before
Mastery levels
5
4
3
2
1
0
Roles
Assassin
Fighter
Mage
Marksman
Melee
Support
Tank
Grades
S+
S
S-
A+
A
A-
B+
B
B-
C+
C
C-
D+
D
D-
Ø
Sort
Group
Group by
None
Free
Chest granted
Custom list
Last played (day)
Last played (week)
Last played (month)
Level
Role
Best grade


Front-end component library
Bootstrap is an open source toolkit for developing with HTML, CSS, and JS. Quickly prototype your ideas or build your entire app with our Sass variables and mixins, responsive grid system, extensive prebuilt components, and powerful plugins built on jQuery.
Bootstrap’s grid system uses a series of containers, rows, and columns to layout and align content. It’s built with flexbox and is fully responsive. Below is an example and an in-depth look at how the grid comes together.
The Boostrap team reviews every new theme and update against a set of guidelines we wrote. These cover tons of factors including design, component flexibility, class naming, directory structure, build tooling, plugin reusability…the list goes on and on.


Koan Loc
Home
Portfolio
Contact
Contact
First Name
Last Name
Email Address
Leave a Message
Leave a Message
Send