Is it possible to use a lottie animation as a mask-image property on an image?
This is my code:
const newData = {
"v": "5.10.2",
"fr": 60,
"ip": 0,
"op": 61,
"w": 500,
"h": 500,
"nm": "Zeichenfläche – 2",
"ddd": 0,
"assets": [],
"layers": [{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "211120_Birgit-Brandis_1216_AS72265r",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [250, 250, 0],
"ix": 2,
"l": 2
},
"a": {
"a": 0,
"k": [182.5, 181.999, 0],
"ix": 1,
"l": 2
},
"s": {
"a": 0,
"k": [100, 100, 100],
"ix": 6,
"l": 2
}
},
"ao": 0,
"shapes": [{
"ty": "gr",
"it": [{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 1,
"k": [{
"i": {
"x": 0.05,
"y": 1
},
"o": {
"x": 0.5,
"y": 0
},
"t": 0,
"s": [{
"i": [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[-1.361, -1.076],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
],
"o": [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 4.649],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
],
"v": [
[365, 247.614],
[349.777, 152.992],
[297.992, 152.991],
[312.734, 84.458],
[312.734, 0.162],
[265.372, 0.162],
[250.757, 0.162],
[181.461, 0.152],
[65.961, 0.002],
[35.985, 0.001],
[0, 0],
[0, 126.637],
[2.322, 136.401],
[14.262, 145.847],
[15.552, 154.496],
[17.362, 166.638],
[17.362, 166.64],
[0.047, 166.64],
[0.047, 286.072],
[0.047, 362.438],
[14.731, 362.697],
[39.602, 363.136],
[253.728, 363.836],
[253.728, 363.635],
[365, 363.999]
],
"c": true
}]
}, {
"t": 60,
"s": [{
"i": [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0.303, -3.151],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[-3.261, -3.473],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
],
"o": [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 4.649],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[5.308, 5.652],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
],
"v": [
[365, 247.614],
[365.027, 153.242],
[364.992, 152.991],
[365.234, 85.958],
[365.234, 50.412],
[343.52, 32.444],
[343.007, 24.162],
[301.211, 24.162],
[63.711, 24.25],
[35.485, 0],
[0, 0],
[0, 126.637],
[-0.053, 137.526],
[0.012, 147.222],
[0.052, 154.746],
[0.237, 166.763],
[0.112, 166.765],
[0.047, 166.64],
[0.047, 278.999],
[22.297, 348.688],
[27.011, 359.347],
[39.602, 363.761],
[253.727, 363.836],
[325.977, 364.01],
[365, 345.999]
],
"c": true
}]
}],
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
}, {
"ty": "fl",
"c": {
"a": 0,
"k": [0, 0, 0, 1],
"ix": 4
},
"o": {
"a": 0,
"k": 100,
"ix": 5
},
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
}, {
"ty": "tr",
"p": {
"a": 0,
"k": [0, 0],
"ix": 2
},
"a": {
"a": 0,
"k": [0, 0],
"ix": 1
},
"s": {
"a": 0,
"k": [100, 100],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "Transform"
}],
"nm": "211120_Birgit-Brandis_1216_AS72265r",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}],
"ip": 0,
"op": 3600,
"st": 0,
"ct": 1,
"bm": 0
}],
"markers": []
}
var params = {
container: document.getElementById('lottie'),
renderer: 'svg',
loop: false,
autoplay: false,
animationData: newData,
rendererSettings: {
preserveAspectRatio: 'none',
id: 'mask',
},
};
var anim;
anim = bodymovin.loadAnimation(params);
let lottie = document.querySelector('#lottie')
function playLottie(direction) {
if (direction == 'f') {
anim.setDirection(1)
} else {
anim.setDirection(-1)
}
anim.play()
}
lottie.addEventListener('mouseover', function() {
playLottie('f')
})
lottie.addEventListener('mouseout', function() {
playLottie('b')
})
#container {
display: flex;
}
#lottie {
width: 300px;
height: 500px;
}
#masked {
mask-repeat: no-repeat;
mask-image: url(#mask);
mask-size: 100% 100%;
mask-position: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.9.1/lottie.min.js"></script>
<div id="container">
<img src="https://picsum.photos/300/500" />
<div id="lottie"></div>
<img id="masked" src="https://picsum.photos/300/500" />
</div>
Alternatively see this codepen: https://codepen.io/jacquesknie/pen/WNVGdWW
The first HTML-element is an image, the second element is a lottie-animation which starts its animation on mouseover, the third element is the same image with the lottie-animation as its mask-image. Unfortunately this doesn't work. The idea is to have an animated mask on the image on mouseover.
Is there any way to make this work? Maybe even with a very different approach?
Thanks in advance!
You can, but you need to ensure you get/create a SVG <mask>
element.
In short you can reference a mask via:
<mask>
In this case you can move the dynamically created animated SVG to a new <mask>
element.
// add event listeners to image
masked.addEventListener("mouseover", function() {
playLottie("f");
});
masked.addEventListener("mouseout", function() {
playLottie("b");
});
// get SVG
let lottieSvg = lottie.querySelector('svg')
//hide lottie svg
lottieSvg.setAttribute('style', `width:0; height:0; position:absolute;`)
let ns = 'http://www.w3.org/2000/svg';
// create SVG mask
let maskEl = document.createElementNS(ns, 'mask')
lottieSvg.append(maskEl)
// add mask bg
let rect = document.createElementNS(ns, 'rect')
rect.setAttribute('fill', '#000')
rect.setAttribute('width', '100%')
rect.setAttribute('height', '100%')
maskEl.append(rect)
maskEl.id = 'mask';
// move outermost group to mask
let gMain = lottie.querySelector('g')
maskEl.append(gMain)
gMain.removeAttribute('clip-path')
let path = lottie.querySelector('path')
path.setAttribute('fill', '#fff')
body {
background: #ccc
}
#masked {
mask-repeat: no-repeat;
mask-image: url(#mask);
mask-size: 100% 100%;
mask-position: center;
mask-mode: luminance;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.9.1/lottie.min.js"></script>
<div id="container">
<div id="lottie"></div>
<img id="masked" src="https://picsum.photos/id/237/300/500" />
</div>
<script>
const newData = {
v: "5.10.2",
fr: 60,
ip: 0,
op: 61,
w: 500,
h: 500,
nm: "Zeichenfläche – 2",
ddd: 0,
assets: [],
layers: [{
ddd: 0,
ind: 1,
ty: 4,
nm: "211120_Birgit-Brandis_1216_AS72265r",
sr: 1,
ks: {
o: {
a: 0,
k: 100,
ix: 11
},
r: {
a: 0,
k: 0,
ix: 10
},
p: {
a: 0,
k: [250, 250, 0],
ix: 2,
l: 2
},
a: {
a: 0,
k: [182.5, 181.999, 0],
ix: 1,
l: 2
},
s: {
a: 0,
k: [100, 100, 100],
ix: 6,
l: 2
}
},
ao: 0,
shapes: [{
ty: "gr",
it: [{
ind: 0,
ty: "sh",
ix: 1,
ks: {
a: 1,
k: [{
i: {
x: 0.05,
y: 1
},
o: {
x: 0.5,
y: 0
},
t: 0,
s: [{
i: [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[-1.361, -1.076],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
],
o: [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 4.649],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
],
v: [
[365, 247.614],
[349.777, 152.992],
[297.992, 152.991],
[312.734, 84.458],
[312.734, 0.162],
[265.372, 0.162],
[250.757, 0.162],
[181.461, 0.152],
[65.961, 0.002],
[35.985, 0.001],
[0, 0],
[0, 126.637],
[2.322, 136.401],
[14.262, 145.847],
[15.552, 154.496],
[17.362, 166.638],
[17.362, 166.64],
[0.047, 166.64],
[0.047, 286.072],
[0.047, 362.438],
[14.731, 362.697],
[39.602, 363.136],
[253.728, 363.836],
[253.728, 363.635],
[365, 363.999]
],
c: true
}]
},
{
t: 60,
s: [{
i: [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0.303, -3.151],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[-3.261, -3.473],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
],
o: [
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 4.649],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[5.308, 5.652],
[0, 0],
[0, 0],
[0, 0],
[0, 0]
],
v: [
[365, 247.614],
[365.027, 153.242],
[364.992, 152.991],
[365.234, 85.958],
[365.234, 50.412],
[343.52, 32.444],
[343.007, 24.162],
[301.211, 24.162],
[63.711, 24.25],
[35.485, 0],
[0, 0],
[0, 126.637],
[-0.053, 137.526],
[0.012, 147.222],
[0.052, 154.746],
[0.237, 166.763],
[0.112, 166.765],
[0.047, 166.64],
[0.047, 278.999],
[22.297, 348.688],
[27.011, 359.347],
[39.602, 363.761],
[253.727, 363.836],
[325.977, 364.01],
[365, 345.999]
],
c: true
}]
}
],
ix: 2
},
nm: "Path 1",
mn: "ADBE Vector Shape - Group",
hd: false
},
{
ty: "fl",
c: {
a: 0,
k: [0, 0, 0, 1],
ix: 4
},
o: {
a: 0,
k: 100,
ix: 5
},
r: 1,
bm: 0,
nm: "Fill 1",
mn: "ADBE Vector Graphic - Fill",
hd: false
},
{
ty: "tr",
p: {
a: 0,
k: [0, 0],
ix: 2
},
a: {
a: 0,
k: [0, 0],
ix: 1
},
s: {
a: 0,
k: [100, 100],
ix: 3
},
r: {
a: 0,
k: 0,
ix: 6
},
o: {
a: 0,
k: 100,
ix: 7
},
sk: {
a: 0,
k: 0,
ix: 4
},
sa: {
a: 0,
k: 0,
ix: 5
},
nm: "Transform"
}
],
nm: "211120_Birgit-Brandis_1216_AS72265r",
np: 2,
cix: 2,
bm: 0,
ix: 1,
mn: "ADBE Vector Group",
hd: false
}],
ip: 0,
op: 3600,
st: 0,
ct: 1,
bm: 0
}],
markers: []
};
var params = {
container: document.getElementById("lottie"),
renderer: "svg",
loop: false,
autoplay: false,
animationData: newData,
rendererSettings: {
preserveAspectRatio: "none",
id: "maskEl"
}
};
var anim;
anim = bodymovin.loadAnimation(params);
let lottie = document.querySelector("#lottie");
function playLottie(direction) {
if (direction == "f") {
anim.setDirection(1);
} else {
anim.setDirection(-1);
}
anim.play();
}
</script>
You also need to bind the animation event with the masked element as the mask itself isn't rendered and therefore not introduce a hit-area.
masked.addEventListener("mouseover", function() {
playLottie("f");
});
I highly recommend to first test masks (or clip-paths) with simplified HTML output before combining it with complex framework/library created elements.