1cb93a386Sopenharmony_cidescribe('Skottie behavior', () => { 2cb93a386Sopenharmony_ci let container; 3cb93a386Sopenharmony_ci 4cb93a386Sopenharmony_ci beforeEach(async () => { 5cb93a386Sopenharmony_ci await LoadCanvasKit; 6cb93a386Sopenharmony_ci container = document.createElement('div'); 7cb93a386Sopenharmony_ci container.innerHTML = ` 8cb93a386Sopenharmony_ci <canvas width=600 height=600 id=test></canvas> 9cb93a386Sopenharmony_ci <canvas width=600 height=600 id=report></canvas>`; 10cb93a386Sopenharmony_ci document.body.appendChild(container); 11cb93a386Sopenharmony_ci }); 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ci afterEach(() => { 14cb93a386Sopenharmony_ci document.body.removeChild(container); 15cb93a386Sopenharmony_ci }); 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_ci const expectArrayCloseTo = (a, b, precision) => { 18cb93a386Sopenharmony_ci precision = precision || 14; // digits of precision in base 10 19cb93a386Sopenharmony_ci expect(a.length).toEqual(b.length); 20cb93a386Sopenharmony_ci for (let i=0; i<a.length; i++) { 21cb93a386Sopenharmony_ci expect(a[i]).toBeCloseTo(b[i], precision); 22cb93a386Sopenharmony_ci } 23cb93a386Sopenharmony_ci }; 24cb93a386Sopenharmony_ci 25cb93a386Sopenharmony_ci const imgPromise = fetch('/assets/flightAnim.gif') 26cb93a386Sopenharmony_ci .then((response) => response.arrayBuffer()); 27cb93a386Sopenharmony_ci const jsonPromise = fetch('/assets/animated_gif.json') 28cb93a386Sopenharmony_ci .then((response) => response.text()); 29cb93a386Sopenharmony_ci const washPromise = fetch('/assets/map-shield.json') 30cb93a386Sopenharmony_ci .then((response) => response.text()); 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ci gm('skottie_animgif', (canvas, promises) => { 33cb93a386Sopenharmony_ci if (!CanvasKit.skottie || !CanvasKit.managed_skottie) { 34cb93a386Sopenharmony_ci console.warn('Skipping test because not compiled with skottie'); 35cb93a386Sopenharmony_ci return; 36cb93a386Sopenharmony_ci } 37cb93a386Sopenharmony_ci expect(promises[1]).not.toBe('NOT FOUND'); 38cb93a386Sopenharmony_ci const animation = CanvasKit.MakeManagedAnimation(promises[1], { 39cb93a386Sopenharmony_ci 'flightAnim.gif': promises[0], 40cb93a386Sopenharmony_ci }); 41cb93a386Sopenharmony_ci expect(animation).toBeTruthy(); 42cb93a386Sopenharmony_ci const bounds = CanvasKit.LTRBRect(0, 0, 500, 500); 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ci const size = animation.size(); 45cb93a386Sopenharmony_ci expectArrayCloseTo(size, Float32Array.of(800, 600), 4); 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 48cb93a386Sopenharmony_ci animation.render(canvas, bounds); 49cb93a386Sopenharmony_ci 50cb93a386Sopenharmony_ci // We intentionally make the length of this array 5 and add a sentinel value 51cb93a386Sopenharmony_ci // of 999 so we can make sure the bounds are copied into this rect and a new 52cb93a386Sopenharmony_ci // one is not allocated. 53cb93a386Sopenharmony_ci const damageRect = Float32Array.of(0, 0, 0, 0, 999); 54cb93a386Sopenharmony_ci 55cb93a386Sopenharmony_ci // There was a bug, fixed in https://skia-review.googlesource.com/c/skia/+/241757 56cb93a386Sopenharmony_ci // that seeking again and drawing again revealed. 57cb93a386Sopenharmony_ci animation.seek(0.5, damageRect); 58cb93a386Sopenharmony_ci expectArrayCloseTo(damageRect, Float32Array.of(0, 0, 800, 600, 999), 4); 59cb93a386Sopenharmony_ci 60cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 61cb93a386Sopenharmony_ci animation.render(canvas, bounds); 62cb93a386Sopenharmony_ci animation.delete(); 63cb93a386Sopenharmony_ci }, imgPromise, jsonPromise); 64cb93a386Sopenharmony_ci 65cb93a386Sopenharmony_ci gm('skottie_setcolor', (canvas, promises) => { 66cb93a386Sopenharmony_ci if (!CanvasKit.skottie || !CanvasKit.managed_skottie) { 67cb93a386Sopenharmony_ci console.warn('Skipping test because not compiled with skottie'); 68cb93a386Sopenharmony_ci return; 69cb93a386Sopenharmony_ci } 70cb93a386Sopenharmony_ci expect(promises[0]).not.toBe('NOT FOUND'); 71cb93a386Sopenharmony_ci const bounds = CanvasKit.LTRBRect(0, 0, 500, 500); 72cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 73cb93a386Sopenharmony_ci 74cb93a386Sopenharmony_ci const animation = CanvasKit.MakeManagedAnimation(promises[0]); 75cb93a386Sopenharmony_ci expect(animation).toBeTruthy(); 76cb93a386Sopenharmony_ci animation.setColor('$Icon Fill', CanvasKit.RED); 77cb93a386Sopenharmony_ci animation.seek(0.5); 78cb93a386Sopenharmony_ci animation.render(canvas, bounds); 79cb93a386Sopenharmony_ci animation.delete(); 80cb93a386Sopenharmony_ci }, washPromise); 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_ci it('can load audio assets', (done) => { 83cb93a386Sopenharmony_ci if (!CanvasKit.skottie || !CanvasKit.managed_skottie) { 84cb93a386Sopenharmony_ci console.warn('Skipping test because not compiled with skottie'); 85cb93a386Sopenharmony_ci return; 86cb93a386Sopenharmony_ci } 87cb93a386Sopenharmony_ci const mockSoundMap = { 88cb93a386Sopenharmony_ci map : new Map(), 89cb93a386Sopenharmony_ci getPlayer : function(name) {return this.map.get(name)}, 90cb93a386Sopenharmony_ci setPlayer : function(name, player) {this.map.set(name, player)}, 91cb93a386Sopenharmony_ci }; 92cb93a386Sopenharmony_ci function mockPlayer(name) { 93cb93a386Sopenharmony_ci this.name = name; 94cb93a386Sopenharmony_ci this.wasPlayed = false, 95cb93a386Sopenharmony_ci this.seek = function(t) { 96cb93a386Sopenharmony_ci this.wasPlayed = true; 97cb93a386Sopenharmony_ci } 98cb93a386Sopenharmony_ci } 99cb93a386Sopenharmony_ci for (let i = 0; i < 20; i++) { 100cb93a386Sopenharmony_ci var name = 'audio_' + i; 101cb93a386Sopenharmony_ci mockSoundMap.setPlayer(name, new mockPlayer(name)); 102cb93a386Sopenharmony_ci } 103cb93a386Sopenharmony_ci fetch('/assets/audio_external.json') 104cb93a386Sopenharmony_ci .then((response) => response.text()) 105cb93a386Sopenharmony_ci .then((lottie) => { 106cb93a386Sopenharmony_ci const animation = CanvasKit.MakeManagedAnimation(lottie, null, null, mockSoundMap); 107cb93a386Sopenharmony_ci expect(animation).toBeTruthy(); 108cb93a386Sopenharmony_ci // 190 frames in sample lottie 109cb93a386Sopenharmony_ci for (let t = 0; t < 190; t++) { 110cb93a386Sopenharmony_ci animation.seekFrame(t); 111cb93a386Sopenharmony_ci } 112cb93a386Sopenharmony_ci animation.delete(); 113cb93a386Sopenharmony_ci for(const player of mockSoundMap.map.values()) { 114cb93a386Sopenharmony_ci expect(player.wasPlayed).toBeTrue(player.name + " was not played"); 115cb93a386Sopenharmony_ci } 116cb93a386Sopenharmony_ci done(); 117cb93a386Sopenharmony_ci }); 118cb93a386Sopenharmony_ci }); 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci it('can get logs', (done) => { 121cb93a386Sopenharmony_ci if (!CanvasKit.skottie || !CanvasKit.managed_skottie) { 122cb93a386Sopenharmony_ci console.warn('Skipping test because not compiled with skottie'); 123cb93a386Sopenharmony_ci return; 124cb93a386Sopenharmony_ci } 125cb93a386Sopenharmony_ci 126cb93a386Sopenharmony_ci const logger = { 127cb93a386Sopenharmony_ci errors: [], 128cb93a386Sopenharmony_ci warnings: [], 129cb93a386Sopenharmony_ci 130cb93a386Sopenharmony_ci reset: function() { this.errors = []; this.warnings = []; }, 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci // Logger API 133cb93a386Sopenharmony_ci onError: function(err) { this.errors.push(err) }, 134cb93a386Sopenharmony_ci onWarning: function(wrn) { this.warnings.push(wrn) } 135cb93a386Sopenharmony_ci }; 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci { 138cb93a386Sopenharmony_ci const json = `{ 139cb93a386Sopenharmony_ci "v": "5.2.1", 140cb93a386Sopenharmony_ci "w": 100, 141cb93a386Sopenharmony_ci "h": 100, 142cb93a386Sopenharmony_ci "fr": 10, 143cb93a386Sopenharmony_ci "ip": 0, 144cb93a386Sopenharmony_ci "op": 100, 145cb93a386Sopenharmony_ci "layers": [{ 146cb93a386Sopenharmony_ci "ty": 3, 147cb93a386Sopenharmony_ci "nm": "null", 148cb93a386Sopenharmony_ci "ind": 0, 149cb93a386Sopenharmony_ci "ip": 0 150cb93a386Sopenharmony_ci }] 151cb93a386Sopenharmony_ci }`; 152cb93a386Sopenharmony_ci const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger); 153cb93a386Sopenharmony_ci expect(animation).toBeTruthy(); 154cb93a386Sopenharmony_ci expect(logger.errors.length).toEqual(0); 155cb93a386Sopenharmony_ci expect(logger.warnings.length).toEqual(0); 156cb93a386Sopenharmony_ci } 157cb93a386Sopenharmony_ci 158cb93a386Sopenharmony_ci { 159cb93a386Sopenharmony_ci const json = `{ 160cb93a386Sopenharmony_ci "v": "5.2.1", 161cb93a386Sopenharmony_ci "w": 100, 162cb93a386Sopenharmony_ci "h": 100, 163cb93a386Sopenharmony_ci "fr": 10, 164cb93a386Sopenharmony_ci "ip": 0, 165cb93a386Sopenharmony_ci "op": 100, 166cb93a386Sopenharmony_ci "layers": [{ 167cb93a386Sopenharmony_ci "ty": 2, 168cb93a386Sopenharmony_ci "nm": "image", 169cb93a386Sopenharmony_ci "ind": 0, 170cb93a386Sopenharmony_ci "ip": 0 171cb93a386Sopenharmony_ci }] 172cb93a386Sopenharmony_ci }`; 173cb93a386Sopenharmony_ci const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger); 174cb93a386Sopenharmony_ci expect(animation).toBeTruthy(); 175cb93a386Sopenharmony_ci expect(logger.errors.length).toEqual(1); 176cb93a386Sopenharmony_ci expect(logger.warnings.length).toEqual(0); 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_ci // Image layer missing refID 179cb93a386Sopenharmony_ci expect(logger.errors[0].includes('missing ref')); 180cb93a386Sopenharmony_ci logger.reset(); 181cb93a386Sopenharmony_ci } 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_ci { 184cb93a386Sopenharmony_ci const json = `{ 185cb93a386Sopenharmony_ci "v": "5.2.1", 186cb93a386Sopenharmony_ci "w": 100, 187cb93a386Sopenharmony_ci "h": 100, 188cb93a386Sopenharmony_ci "fr": 10, 189cb93a386Sopenharmony_ci "ip": 0, 190cb93a386Sopenharmony_ci "op": 100, 191cb93a386Sopenharmony_ci "layers": [{ 192cb93a386Sopenharmony_ci "ty": 1, 193cb93a386Sopenharmony_ci "nm": "solid", 194cb93a386Sopenharmony_ci "sw": 100, 195cb93a386Sopenharmony_ci "sh": 100, 196cb93a386Sopenharmony_ci "sc": "#aabbcc", 197cb93a386Sopenharmony_ci "ind": 0, 198cb93a386Sopenharmony_ci "ip": 0, 199cb93a386Sopenharmony_ci "ef": [{ 200cb93a386Sopenharmony_ci "mn": "FOO" 201cb93a386Sopenharmony_ci }] 202cb93a386Sopenharmony_ci }] 203cb93a386Sopenharmony_ci }`; 204cb93a386Sopenharmony_ci const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger); 205cb93a386Sopenharmony_ci expect(animation).toBeTruthy(); 206cb93a386Sopenharmony_ci expect(logger.errors.length).toEqual(0); 207cb93a386Sopenharmony_ci expect(logger.warnings.length).toEqual(1); 208cb93a386Sopenharmony_ci 209cb93a386Sopenharmony_ci // Unsupported effect FOO 210cb93a386Sopenharmony_ci expect(logger.warnings[0].includes('FOO')); 211cb93a386Sopenharmony_ci logger.reset(); 212cb93a386Sopenharmony_ci } 213cb93a386Sopenharmony_ci 214cb93a386Sopenharmony_ci done(); 215cb93a386Sopenharmony_ci }); 216cb93a386Sopenharmony_ci 217cb93a386Sopenharmony_ci it('can access dynamic props', () => { 218cb93a386Sopenharmony_ci if (!CanvasKit.skottie || !CanvasKit.managed_skottie) { 219cb93a386Sopenharmony_ci console.warn('Skipping test because not compiled with skottie'); 220cb93a386Sopenharmony_ci return; 221cb93a386Sopenharmony_ci } 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci const json = `{ 224cb93a386Sopenharmony_ci "v": "5.2.1", 225cb93a386Sopenharmony_ci "w": 100, 226cb93a386Sopenharmony_ci "h": 100, 227cb93a386Sopenharmony_ci "fr": 10, 228cb93a386Sopenharmony_ci "ip": 0, 229cb93a386Sopenharmony_ci "op": 100, 230cb93a386Sopenharmony_ci "fonts": { 231cb93a386Sopenharmony_ci "list": [{ 232cb93a386Sopenharmony_ci "fName": "test_font", 233cb93a386Sopenharmony_ci "fFamily": "test-family", 234cb93a386Sopenharmony_ci "fStyle": "TestFontStyle" 235cb93a386Sopenharmony_ci }] 236cb93a386Sopenharmony_ci }, 237cb93a386Sopenharmony_ci "layers": [ 238cb93a386Sopenharmony_ci { 239cb93a386Sopenharmony_ci "ty": 4, 240cb93a386Sopenharmony_ci "nm": "__shape_layer", 241cb93a386Sopenharmony_ci "ind": 0, 242cb93a386Sopenharmony_ci "ip": 0, 243cb93a386Sopenharmony_ci "shapes": [ 244cb93a386Sopenharmony_ci { 245cb93a386Sopenharmony_ci "ty": "el", 246cb93a386Sopenharmony_ci "p": { "a": 0, "k": [ 50, 50 ] }, 247cb93a386Sopenharmony_ci "s": { "a": 0, "k": [ 50, 50 ] } 248cb93a386Sopenharmony_ci },{ 249cb93a386Sopenharmony_ci "ty": "fl", 250cb93a386Sopenharmony_ci "nm": "__shape_fill", 251cb93a386Sopenharmony_ci "c": { "a": 0, "k": [ 1, 0, 0] } 252cb93a386Sopenharmony_ci },{ 253cb93a386Sopenharmony_ci "ty": "tr", 254cb93a386Sopenharmony_ci "nm": "__shape_opacity", 255cb93a386Sopenharmony_ci "o": { "a": 0, "k": 50 } 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci ] 258cb93a386Sopenharmony_ci },{ 259cb93a386Sopenharmony_ci "ty": 5, 260cb93a386Sopenharmony_ci "nm": "__text_layer", 261cb93a386Sopenharmony_ci "ip": 0, 262cb93a386Sopenharmony_ci "t": { 263cb93a386Sopenharmony_ci "d": { 264cb93a386Sopenharmony_ci "k": [{ 265cb93a386Sopenharmony_ci "t": 0, 266cb93a386Sopenharmony_ci "s": { 267cb93a386Sopenharmony_ci "f": "test_font", 268cb93a386Sopenharmony_ci "s": 100, 269cb93a386Sopenharmony_ci "t": "Foo Bar Baz", 270cb93a386Sopenharmony_ci "lh": 120, 271cb93a386Sopenharmony_ci "ls": 12 272cb93a386Sopenharmony_ci } 273cb93a386Sopenharmony_ci }] 274cb93a386Sopenharmony_ci } 275cb93a386Sopenharmony_ci } 276cb93a386Sopenharmony_ci } 277cb93a386Sopenharmony_ci ] 278cb93a386Sopenharmony_ci }`; 279cb93a386Sopenharmony_ci 280cb93a386Sopenharmony_ci const animation = CanvasKit.MakeManagedAnimation(json, null, '__'); 281cb93a386Sopenharmony_ci expect(animation).toBeTruthy(); 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ci { 284cb93a386Sopenharmony_ci const colors = animation.getColorProps(); 285cb93a386Sopenharmony_ci expect(colors.length).toEqual(1); 286cb93a386Sopenharmony_ci expect(colors[0].key).toEqual('__shape_fill'); 287cb93a386Sopenharmony_ci expect(colors[0].value).toEqual(CanvasKit.ColorAsInt(255,0,0,255)); 288cb93a386Sopenharmony_ci 289cb93a386Sopenharmony_ci const opacities = animation.getOpacityProps(); 290cb93a386Sopenharmony_ci expect(opacities.length).toEqual(1); 291cb93a386Sopenharmony_ci expect(opacities[0].key).toEqual('__shape_opacity'); 292cb93a386Sopenharmony_ci expect(opacities[0].value).toEqual(50); 293cb93a386Sopenharmony_ci 294cb93a386Sopenharmony_ci const texts = animation.getTextProps(); 295cb93a386Sopenharmony_ci expect(texts.length).toEqual(1); 296cb93a386Sopenharmony_ci expect(texts[0].key).toEqual('__text_layer'); 297cb93a386Sopenharmony_ci expect(texts[0].value.text).toEqual('Foo Bar Baz'); 298cb93a386Sopenharmony_ci expect(texts[0].value.size).toEqual(100); 299cb93a386Sopenharmony_ci } 300cb93a386Sopenharmony_ci 301cb93a386Sopenharmony_ci expect(animation.setColor('__shape_fill', [0,1,0,1])).toEqual(true); 302cb93a386Sopenharmony_ci expect(animation.setOpacity('__shape_opacity', 100)).toEqual(true); 303cb93a386Sopenharmony_ci expect(animation.setText('__text_layer', 'baz bar foo', 10)).toEqual(true); 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_ci { 306cb93a386Sopenharmony_ci const colors = animation.getColorProps(); 307cb93a386Sopenharmony_ci expect(colors.length).toEqual(1); 308cb93a386Sopenharmony_ci expect(colors[0].key).toEqual('__shape_fill'); 309cb93a386Sopenharmony_ci expect(colors[0].value).toEqual(CanvasKit.ColorAsInt(0,255,0,255)); 310cb93a386Sopenharmony_ci 311cb93a386Sopenharmony_ci const opacities = animation.getOpacityProps(); 312cb93a386Sopenharmony_ci expect(opacities.length).toEqual(1); 313cb93a386Sopenharmony_ci expect(opacities[0].key).toEqual('__shape_opacity'); 314cb93a386Sopenharmony_ci expect(opacities[0].value).toEqual(100); 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci const texts = animation.getTextProps(); 317cb93a386Sopenharmony_ci expect(texts.length).toEqual(1); 318cb93a386Sopenharmony_ci expect(texts[0].key).toEqual('__text_layer'); 319cb93a386Sopenharmony_ci expect(texts[0].value.text).toEqual('baz bar foo'); 320cb93a386Sopenharmony_ci expect(texts[0].value.size).toEqual(10); 321cb93a386Sopenharmony_ci } 322cb93a386Sopenharmony_ci 323cb93a386Sopenharmony_ci expect(animation.setColor('INVALID_KEY', [0,1,0,1])).toEqual(false); 324cb93a386Sopenharmony_ci expect(animation.setOpacity('INVALID_KEY', 100)).toEqual(false); 325cb93a386Sopenharmony_ci expect(animation.setText('INVALID KEY', '', 10)).toEqual(false); 326cb93a386Sopenharmony_ci }); 327cb93a386Sopenharmony_ci}); 328