Most of the shapes in Carnac can be animated using AnimationStyle, which internally uses AbstractEffects. Every effect can animate only one property of a shape. Parameters of effect are very similar to the SMIL animation. This tutorial includes several examples of animation below.
# Color Animation
This animation example changes the color of the shape.
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
function createScene(canvas) {
const color = new Rectangle({
"left": 50,
"top": 10,
"width": 200,
"height": 100,
"linestyle": {
color: KnownColors.Orange,
width: 3
},
"fillstyle": KnownColors.Blue,
"animationstyle": {
"attributename": "fill",
"duration": 1e3,
"repeatcount": Infinity,
"from": KnownColors.Blue,
"to": KnownColors.Orange
}
});
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild(color)
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Combined Animation
Animation effects can be run one by one.
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { AnimationStyle } from "@int/geotoolkit/attributes/AnimationStyle.ts";
import { AnimationFill } from "@int/geotoolkit/animation/AnimationFill.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
function createScene(canvas) {
const rectangle = new Rectangle({
"left": 50,
"top": 50,
"width": 10,
"height": 50,
"linestyle": {
color: KnownColors.Orange,
width: 3
},
"fillstyle": KnownColors.Blue
});
const combineAnimation = new AnimationStyle([{
"attributename": "width",
"fill": AnimationFill.Freeze,
"begin": "2s;fill.end",
"duration": 1e3,
"id": "width",
"from": 10,
"to": 100
}, {
"id": "fill",
"attributename": "fill",
"begin": "width.end+1s",
"duration": 1e3,
"from": KnownColors.Blue,
"to": KnownColors.Yellow
}]);
rectangle.setAnimationStyle(combineAnimation);
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild(rectangle)
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Complex Animation
Animation style can contain several effects, which would be animated simultaneously.
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
function createScene(canvas) {
const complex = new Rectangle({
"left": 0,
"top": 100,
"width": 50,
"height": 50,
"linestyle": {
color: KnownColors.Orange,
width: 3
},
"fillstyle": KnownColors.Blue,
"animationstyle": [{
"attributename": "transform",
"type": "rotate",
"duration": 2e3,
"repeatcount": Infinity,
"from": 0,
"to": 360
}, {
"attributename": "x",
"duration": 2e3,
"repeatcount": Infinity,
"values": [0, 300]
}, {
"attributename": "y",
"duration": 2e3,
"repeatcount": Infinity,
"values": [100, 10, 100]
}]
});
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild(complex)
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Custom Animation
Animation style can also animate custom properties. For this there are 2 requirements
- Animated property can be got/set with getProperties/setProperties
- Type of animated property should be Number
For example, on Arc we can animate radius and startArc/endArc properties.
.cg-tooltip-holder {
position: relative;
}
.cg-tooltip {
position: absolute;
display: block;
padding: 2px 12px 3px 7px;
overflow: visible !important;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
background: white !important;
opacity: 0.9;
color: #333333;
border: solid 1px gray;
border-radius: 5px;
text-align: left;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
border-radius: 5px;
margin: 0 !important;
z-index: 10000;
max-width: 400px;
text-wrap: normal !important;
white-space: normal !important;
}
/* Default setting for tooltip */
.cg-tooltip-container {
position: absolute;
display: block;
overflow: visible !important;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 12px;
padding: 3px 7px;
background: #f7f7f7;
color: #333333;
border: 1px solid #938e8e;
opacity: 0.8;
text-align: left;
box-shadow: 3px 3px 10px #888;
margin: 0 !important;
z-index: 10000;
max-width: 400px;
text-wrap: normal !important;
white-space: normal !important;
user-select: none;
}
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.cg-tooltip-container {
border-radius: 0;
}
}
/* Default left arrow for tooltip */
.cg-tooltip-arrow-left::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 0;
top: 50%;
border: 5px solid transparent;
border-left: 0;
border-right: 5px solid #938e8e;
transform: translate(calc(-100%), -50%);
}
.cg-tooltip-arrow-left::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 0;
top: 50%;
border: 4px solid transparent;
border-left: 0;
border-right: 4px solid #f7f7f7;
transform: translate(calc(-100%), -50%);
}
/* Default top arrow for tooltip */
.cg-tooltip-arrow-top::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
top: 0;
border: 5px solid transparent;
border-top: 0;
border-bottom: 5px solid #938e8e;
transform: translate(-50%, -100%);
}
.cg-tooltip-arrow-top::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
top: 0;
border: 4px solid transparent;
border-top: 0;
border-bottom: 4px solid #f7f7f7;
transform: translate(-50%, -100%);
}
/* Default right arrow for tooltip */
.cg-tooltip-arrow-right::before {
content: '';
position: absolute;
display: block;
width: 0px;
right: 0;
top: 50%;
border: 5px solid transparent;
border-right: 0;
border-left: 5px solid #938e8e;
transform: translate(100%, -50%);
}
.cg-tooltip-arrow-right::after {
content: '';
position: absolute;
display: block;
width: 0px;
right: 0;
top: 50%;
border: 4px solid transparent;
border-right: 0;
border-left: 4px solid #f7f7f7;
transform: translate(100%, -50%);
}
/* Default bottom arrow for tooltip */
.cg-tooltip-arrow-bottom::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
bottom: 0px;
border: 5px solid transparent;
border-bottom: 0;
border-top: 5px solid #938e8e;
transform: translate(-50%, 100%);
z-index: 10000;
}
.cg-tooltip-arrow-bottom::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
bottom: 0;
border: 4px solid transparent;
border-bottom: 0;
border-top: 4px solid #f7f7f7;
transform: translate(-50%, 100%);
z-index: 10000;
}
/* Tooltip item name */
/* Tooltip item value */
/* .cg-tooltip-item-value */
/* Tooltip item value */
.cg-tooltip-item-unit {
text-transform: none;
}
.cg-tooltip-item-name {
text-transform: capitalize;
white-space: nowrap;
vertical-align: middle;
font-size: 13px;
}
.cg-tooltip-row {
display: flex;
flex-direction: row;
align-items: center;
white-space: pre-wrap;
font-size: 12px;
line-height: 100%;
margin: 1px 0;
}
.cg-tooltip-title {
font-size: 13px;
height: 14px;
text-transform: capitalize;
}
.cg-tooltip-title .cg-tooltip-symbol {
margin-right: 0 !important;
}
.cg-tooltip-title-name {
vertical-align: middle;
}
.cg-tooltip-row + .cg-tooltip-row {
margin-top: 4px;
}
.cg-tooltip-row.cg-tooltip-title + .cg-tooltip-row {
margin-top: 5px;
}
.cg-tooltip-item-value + .cg-tooltip-item-unit {
margin-left: 1px;
}
/* Tooltip symbol */
.cg-tooltip-symbol-cell {
display: inline-flex;
min-width: 13px; /* 10px size + 3px margin */
}
.cg-tooltip-symbol {
margin-right: 3px;
background-color: transparent;
display: block;
}
.cg-tooltip-symbol > img {
display: block;
}
.cg-tooltip-list-cell {
display: inline-flex;
}
.cg-tooltip-list-symbol {
display: block;
margin-right: 3px;
width: 6px;
height: 6px;
vertical-align: middle;
border-radius: 50%;
border: 1px solid rgba(0,0,0,.4);
}
.cg-tooltip-symbol-legacy {
border-radius: 4px;
margin-right: 5px;
height: 8px;
width: 8px;
display: inline-block;
}
.cg-tooltip-title-legacy {
font-weight: 900;
}
/* Tooltip symbol circle */
.cg-tooltip-symbol.circle {
height: 10px;
width: 10px;
display: inline-block;
border-radius: 50%;
border: 1px solid rgba(0,0,0,.4);
}
/* Tooltip symbol line */
.cg-tooltip-symbol.line {
height: 10px;
width: 10px;
display: inline-block;
transform: scale(1.2, 0.2);
}
/* Tooltip symbol diamond */
.cg-tooltip-symbol.diamond {
height: 10px;
width: 10px;
display: inline-block;
transform: rotate(45deg);
border-radius: 0px;
}
/* Tooltip symbol square */
.cg-tooltip-symbol.square {
height: 10px;
width: 10px;
display: inline-block;
border-radius: 0px;
border: 1px solid rgba(0,0,0,.4);
}
# Custom Effect
This example of custom animation on 3D PieChart shows how to animate inclination and pad of outsideline properties.
import { AbstractProcessor } from "@int/geotoolkit/animation/processors/AbstractProcessor.ts";
import { obfuscate } from "@int/geotoolkit/lib.js";
import { AbstractEffect } from "@int/geotoolkit/animation/effects/AbstractEffect.ts";
import { Registry } from "@int/geotoolkit/animation/effects/Registry.ts";
import { DataMode, LabelLocation, PieMode } from "@int/geotoolkit/controls/shapes/DonutChart.ts";
import { PieChart } from "@int/geotoolkit/controls/shapes/PieChart.ts";
import { AnimationStyle } from "@int/geotoolkit/attributes/AnimationStyle.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { Functions } from "@int/geotoolkit/animation/Easing.ts";
class Processor extends AbstractProcessor {
constructor() {
super();
}
process(from, to, out, func) {
out = out || {};
out["inclination3d"] = func(from["inclination3d"], to["inclination3d"]);
out["outsideline"] = out["outsideline"] || {};
out["outsideline"]["pad"] = func(from["outsideline"]["pad"], to["outsideline"]["pad"]);
return out;
}
clone(value) {
return {
"inclination3d": value["inclination3d"],
"outsideline": {
"pad": value["outsideline"]["pad"]
}
};
}
}
obfuscate(Processor);
class Effect extends AbstractEffect {
constructor(attr) {
super(attr);
this.setProcessor(new Processor());
}
isApplicable(effect, node) {
return effect["attributename"] === "options" && node instanceof PieChart;
}
getAnimatedValue(node) {
const pieChart = node instanceof PieChart ? node : null;
const options = pieChart?.getOptions();
if (!options) {
return null;
}
return {
"inclination3d": options["inclination3d"],
"outsideline": {
"pad": options["outsideline"]["pad"]
}
};
}
apply(node) {
const options = this.getCurrentValue();
const pieChart = node instanceof PieChart ? node : null;
pieChart?.setOptions({
"inclination3d": options["inclination3d"],
"outsideline": {
"pad": options["outsideline"]["pad"]
}
});
}
}
obfuscate(Effect);
Registry.getInstance().registerEffect(Effect);
let idTimeout, idInterval;
function createScene(canvas) {
const values = {
"Chert": 30,
"Shale": 17,
"Salt": 22,
"Limestone": 10
};
const data = {
"mode": DataMode.Associative,
"values": values
};
const options = {
"minpercentage": 2,
"maxslices": 4,
"piemode": PieMode.Pie3D,
"inclination3d": 0,
"depth3d": 50,
"fillstyles": [
KnownColors.Red,
KnownColors.Blue,
KnownColors.Green,
KnownColors.Yellow
],
"label": {
"textstyle": {
"font": "8pt Arial"
},
"location": LabelLocation.Outside
},
"outsideline": {
"length": 15,
"pad": 0
}
};
const widget = new PieChart(options, data);
const animationStyle = new AnimationStyle({
"attributename": "options",
"begin": "auto",
"duration": 1500,
"function": Functions.EaseOutBounce
});
widget.setAnimationStyle(animationStyle);
const plot = new Plot({
"canvaselement": canvas,
"root": widget,
"autorootbounds": false
});
const W = plot.getWidth(), H = plot.getHeight();
const xC = W / 2, yC = H / 2;
widget.setBounds(new Rect(xC - 130, yC - 130, xC + 130, yC + 130));
widget.setOptions({
"inclination3d": 50,
"outsideline": {
"pad": 25
}
});
idInterval = window.setInterval(() => {
widget.setAnimationStyle(null).setVisible(false).setOptions({
"inclination3d": 0,
"outsideline": {
"pad": 0
}
}).setAnimationStyle(animationStyle);
idTimeout = window.setTimeout(() => {
widget.setOptions({
"inclination3d": 50,
"outsideline": {
"pad": 25
}
}).setVisible(true);
}, 300);
}, 4e3);
return plot;
}
function clear() {
clearInterval(idInterval);
clearTimeout(idTimeout);
}
export {
clear,
createScene
};
createScene(document.querySelector('[ref="plot"]'));
# Opacity Animation
This animation example shows how to change the Opacity to create the animation effect.
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { Image } from "@int/geotoolkit/scene/shapes/Image.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import intImage from "/src/assets/images/slb-134x80.png?import";
function createScene(canvas) {
const opacity = [new Rectangle({
"left": 50,
"top": 50,
"width": 50,
"height": 50,
"linestyle": {
color: KnownColors.Orange,
width: 3
},
"fillstyle": KnownColors.Blue,
"animationstyle": {
"attributename": "opacity",
"duration": 1e3,
"repeatcount": Infinity,
"from": 1,
"to": 0
}
}), new Image({
"ax": 120,
"ay": 35,
"url": intImage,
"animationstyle": {
"attributename": "opacity",
"duration": 1e3,
"repeatcount": Infinity,
"from": 1,
"to": 0
}
})];
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild(opacity)
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Position Animation
This animation example uses the EasingFunction to change the position of the shape.
.cg-tooltip-holder {
position: relative;
}
.cg-tooltip {
position: absolute;
display: block;
padding: 2px 12px 3px 7px;
overflow: visible !important;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
background: white !important;
opacity: 0.9;
color: #333333;
border: solid 1px gray;
border-radius: 5px;
text-align: left;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
border-radius: 5px;
margin: 0 !important;
z-index: 10000;
max-width: 400px;
text-wrap: normal !important;
white-space: normal !important;
}
/* Default setting for tooltip */
.cg-tooltip-container {
position: absolute;
display: block;
overflow: visible !important;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 12px;
padding: 3px 7px;
background: #f7f7f7;
color: #333333;
border: 1px solid #938e8e;
opacity: 0.8;
text-align: left;
box-shadow: 3px 3px 10px #888;
margin: 0 !important;
z-index: 10000;
max-width: 400px;
text-wrap: normal !important;
white-space: normal !important;
user-select: none;
}
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.cg-tooltip-container {
border-radius: 0;
}
}
/* Default left arrow for tooltip */
.cg-tooltip-arrow-left::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 0;
top: 50%;
border: 5px solid transparent;
border-left: 0;
border-right: 5px solid #938e8e;
transform: translate(calc(-100%), -50%);
}
.cg-tooltip-arrow-left::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 0;
top: 50%;
border: 4px solid transparent;
border-left: 0;
border-right: 4px solid #f7f7f7;
transform: translate(calc(-100%), -50%);
}
/* Default top arrow for tooltip */
.cg-tooltip-arrow-top::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
top: 0;
border: 5px solid transparent;
border-top: 0;
border-bottom: 5px solid #938e8e;
transform: translate(-50%, -100%);
}
.cg-tooltip-arrow-top::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
top: 0;
border: 4px solid transparent;
border-top: 0;
border-bottom: 4px solid #f7f7f7;
transform: translate(-50%, -100%);
}
/* Default right arrow for tooltip */
.cg-tooltip-arrow-right::before {
content: '';
position: absolute;
display: block;
width: 0px;
right: 0;
top: 50%;
border: 5px solid transparent;
border-right: 0;
border-left: 5px solid #938e8e;
transform: translate(100%, -50%);
}
.cg-tooltip-arrow-right::after {
content: '';
position: absolute;
display: block;
width: 0px;
right: 0;
top: 50%;
border: 4px solid transparent;
border-right: 0;
border-left: 4px solid #f7f7f7;
transform: translate(100%, -50%);
}
/* Default bottom arrow for tooltip */
.cg-tooltip-arrow-bottom::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
bottom: 0px;
border: 5px solid transparent;
border-bottom: 0;
border-top: 5px solid #938e8e;
transform: translate(-50%, 100%);
z-index: 10000;
}
.cg-tooltip-arrow-bottom::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
bottom: 0;
border: 4px solid transparent;
border-bottom: 0;
border-top: 4px solid #f7f7f7;
transform: translate(-50%, 100%);
z-index: 10000;
}
/* Tooltip item name */
/* Tooltip item value */
/* .cg-tooltip-item-value */
/* Tooltip item value */
.cg-tooltip-item-unit {
text-transform: none;
}
.cg-tooltip-item-name {
text-transform: capitalize;
white-space: nowrap;
vertical-align: middle;
font-size: 13px;
}
.cg-tooltip-row {
display: flex;
flex-direction: row;
align-items: center;
white-space: pre-wrap;
font-size: 12px;
line-height: 100%;
margin: 1px 0;
}
.cg-tooltip-title {
font-size: 13px;
height: 14px;
text-transform: capitalize;
}
.cg-tooltip-title .cg-tooltip-symbol {
margin-right: 0 !important;
}
.cg-tooltip-title-name {
vertical-align: middle;
}
.cg-tooltip-row + .cg-tooltip-row {
margin-top: 4px;
}
.cg-tooltip-row.cg-tooltip-title + .cg-tooltip-row {
margin-top: 5px;
}
.cg-tooltip-item-value + .cg-tooltip-item-unit {
margin-left: 1px;
}
/* Tooltip symbol */
.cg-tooltip-symbol-cell {
display: inline-flex;
min-width: 13px; /* 10px size + 3px margin */
}
.cg-tooltip-symbol {
margin-right: 3px;
background-color: transparent;
display: block;
}
.cg-tooltip-symbol > img {
display: block;
}
.cg-tooltip-list-cell {
display: inline-flex;
}
.cg-tooltip-list-symbol {
display: block;
margin-right: 3px;
width: 6px;
height: 6px;
vertical-align: middle;
border-radius: 50%;
border: 1px solid rgba(0,0,0,.4);
}
.cg-tooltip-symbol-legacy {
border-radius: 4px;
margin-right: 5px;
height: 8px;
width: 8px;
display: inline-block;
}
.cg-tooltip-title-legacy {
font-weight: 900;
}
/* Tooltip symbol circle */
.cg-tooltip-symbol.circle {
height: 10px;
width: 10px;
display: inline-block;
border-radius: 50%;
border: 1px solid rgba(0,0,0,.4);
}
/* Tooltip symbol line */
.cg-tooltip-symbol.line {
height: 10px;
width: 10px;
display: inline-block;
transform: scale(1.2, 0.2);
}
/* Tooltip symbol diamond */
.cg-tooltip-symbol.diamond {
height: 10px;
width: 10px;
display: inline-block;
transform: rotate(45deg);
border-radius: 0px;
}
/* Tooltip symbol square */
.cg-tooltip-symbol.square {
height: 10px;
width: 10px;
display: inline-block;
border-radius: 0px;
border: 1px solid rgba(0,0,0,.4);
}
# Size Animation
This animation example shows how to change the attributes height and width to create the animation effect.
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
function createScene(canvas) {
const size = new Rectangle({
"left": 50,
"top": 50,
"width": 5,
"height": 50,
"linestyle": {
color: KnownColors.Orange,
width: 3
},
"fillstyle": KnownColors.Blue,
"animationstyle": [{
"attributename": "width",
"duration": 1e3,
"repeatcount": Infinity,
"from": 5,
"to": 200
}, {
"attributename": "height",
"duration": 1e3,
"repeatcount": Infinity,
"from": 50,
"to": 5
}]
});
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild(size)
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Transformation
This animation example shows how to change the transform attributes to create the animation effect.
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
function createScene(canvas) {
const transformation = new Rectangle({
"left": 50,
"top": 50,
"width": 50,
"height": 50,
"linestyle": {
color: KnownColors.Orange,
width: 3
},
"fillstyle": KnownColors.Blue,
"animationstyle": {
"attributename": "transform",
"type": "translate",
"duration": 1e3,
"repeatcount": Infinity,
"unit": "cm",
"from": { "x": 0, "y": 0 },
"to": { "x": 2, "y": 0 }
}
});
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild(transformation)
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));