Merge branch '67_gradients_canvas'
diff --git a/player/index.html b/player/index.html
index 1e1d0bb..73cde14 100644
--- a/player/index.html
+++ b/player/index.html
@@ -192,14 +192,14 @@
     //elem.style.display = 'none';
     var animData = {
         container: elem,
-        renderer: 'svg',
+        renderer: 'canvas',
         loop: false,
-        autoplay: false,
+        autoplay: true,
         rendererSettings: {
             progressiveLoad:false,
             imagePreserveAspectRatio: 'xMidYMid meet'
         },
-        path: 'exports/render/data.json'
+        path: 'exports/render/data2.json'
     };
     anim = lottie.loadAnimation(animData);
 
diff --git a/player/js/elements/canvasElements/CVShapeElement.js b/player/js/elements/canvasElements/CVShapeElement.js
index b90d66f..299481d 100644
--- a/player/js/elements/canvasElements/CVShapeElement.js
+++ b/player/js/elements/canvasElements/CVShapeElement.js
@@ -33,9 +33,15 @@
         if(!elementData.c.k){
             styleElem.co = 'rgb('+bm_floor(elementData.c.v[0])+','+bm_floor(elementData.c.v[1])+','+bm_floor(elementData.c.v[2])+')';
         }
+    } else if (data.ty === 'gf' || data.ty === 'gs') {
+        elementData.s = PropertyFactory.getProp(this,data.s,1,null,this);
+        elementData.e = PropertyFactory.getProp(this,data.e,1,null,this);
+        elementData.h = PropertyFactory.getProp(this,data.h||{k:0},0,0.01,this);
+        elementData.a = PropertyFactory.getProp(this,data.a||{k:0},0,degToRads,this);
+        elementData.g = new GradientProperty(this,data.g);
     }
     elementData.o = PropertyFactory.getProp(this,data.o,0,0.01,this);
-    if(data.ty == 'st') {
+    if(data.ty == 'st' || data.ty == 'gs') {
         styleElem.lc = this.lcEnum[data.lc] || 'round';
         styleElem.lj = this.ljEnum[data.lj] || 'round';
         if(data.lj == 1) {
@@ -53,9 +59,7 @@
                 styleElem.do = elementData.d.dashoffset[0];
             }
         }
-
     } else {
-
         styleElem.r = data.r === 2 ? 'evenodd' : 'nonzero';
     }
     this.stylesList.push(styleElem);
@@ -133,7 +137,7 @@
         } else {
             itemsData[i] = prevViewData[processedPos - 1];
         }
-        if(arr[i].ty == 'fl' || arr[i].ty == 'st'){
+        if(arr[i].ty == 'fl' || arr[i].ty == 'st'|| arr[i].ty == 'gf'|| arr[i].ty == 'gs'){
             if(!processedPos){
                 itemsData[i] = this.createStyleElement(arr[i]);
             } else {
@@ -228,28 +232,28 @@
     for(i=0;i<len;i+=1){
         currentStyle = this.stylesList[i];
         type = currentStyle.type;
-        if((type === 'st' && currentStyle.wi === 0) || !currentStyle.data._render || currentStyle.coOp === 0){
+        if(((type === 'st' || type === 'gs') && currentStyle.wi === 0) || !currentStyle.data._render || currentStyle.coOp === 0){
             continue;
         }
         renderer.save();
         elems = currentStyle.elements;
-        if(type === 'st'){
-            ctx.strokeStyle = currentStyle.co;
+        if(type === 'st' || type === 'gs'){
+            ctx.strokeStyle = type === 'st' ? currentStyle.co : currentStyle.grd;
             ctx.lineWidth = currentStyle.wi;
             ctx.lineCap = currentStyle.lc;
             ctx.lineJoin = currentStyle.lj;
             ctx.miterLimit = currentStyle.ml || 0;
-        }else{
-            ctx.fillStyle = currentStyle.co;
+        } else {
+            ctx.fillStyle = type === 'fl' ? currentStyle.co : currentStyle.grd;
         }
         renderer.ctxOpacity(currentStyle.coOp);
         if(this.globalData.currentGlobalAlpha !== 0) {
-            if(type !== 'st'){
+            if(type !== 'st' && type !== 'gs'){
                 ctx.beginPath();
             }
             jLen = elems.length;
             for(j=0;j<jLen;j+=1){
-                if(type === 'st'){
+                if(type === 'st' || type === 'gs'){
                     ctx.beginPath();
                     if(currentStyle.da){
                         ctx.setLineDash(currentStyle.da);
@@ -272,11 +276,11 @@
                         ctx.closePath();
                     }
                 }
-                if(type === 'st'){
+                if(type === 'st' || type === 'gs'){
                     ctx.stroke();
                 }
             }
-            if(type !== 'st'){
+            if(type !== 'st' && type !== 'gs'){
                 ctx.fill(currentStyle.r);
             }
             
@@ -299,6 +303,8 @@
             this.renderFill(items[i],data[i],groupTransform);
         }else if(items[i].ty == 'st'){
             this.renderStroke(items[i],data[i],groupTransform);
+        }else if(items[i].ty == 'gf' || items[i].ty == 'gs'){
+            this.renderGradientFill(items[i],data[i],groupTransform);
         }else if(items[i].ty == 'gr'){
             this.renderShape(groupTransform,items[i].it,data[i].it);
         }else if(items[i].ty == 'tm'){
@@ -364,8 +370,6 @@
     }
 };
 
-
-
 CVShapeElement.prototype.renderFill = function(styleData,itemData, groupTransform){
     var styleElem = itemData.style;
 
@@ -377,6 +381,44 @@
     }
 };
 
+CVShapeElement.prototype.renderGradientFill = function(styleData,itemData, groupTransform){
+    var styleElem = itemData.style;
+    if(!styleElem.grd || itemData.g._mdf || itemData.s._mdf || itemData.e._mdf || (styleData.t !== 1 && (itemData.h._mdf || itemData.a._mdf))) {
+        var ctx = this.globalData.canvasContext;
+        var grd;
+        var pt1 = itemData.s.v, pt2 = itemData.e.v;
+        pt1 = groupTransform.mat.applyToPointArray(pt1[0], pt1[1], 0);
+        pt2 = groupTransform.mat.applyToPointArray(pt2[0], pt2[1], 0);
+        if(styleData.t === 1) {
+            grd=ctx.createLinearGradient(pt1[0],pt1[1],pt2[0],pt2[1]);
+        } else {
+            var rad;
+            rad = Math.sqrt(Math.pow(pt1[0] - pt2[0], 2) + Math.pow(pt1[1] - pt2[1], 2));
+            var ang = Math.atan2(pt2[1] - pt1[1], pt2[0] - pt1[0]);
+
+            var percent = itemData.h.v >= 1 ? 0.99 : itemData.h.v <= -1 ? -0.99: itemData.h.v;
+            var dist = rad * percent;
+            var x = Math.cos(ang + itemData.a.v) * dist + pt1[0];
+            var y = Math.sin(ang + itemData.a.v) * dist + pt1[1];
+            var grd=ctx.createRadialGradient(x,y,0,pt1[0],pt1[1],rad);
+        }
+
+        var i, len = styleData.g.p;
+        var cValues = itemData.g.c;
+        var opacity = 1;
+
+        for (i = 0; i < len; i += 1){
+            if(itemData.g._hasOpacity && itemData.g._collapsable) {
+                opacity = itemData.g.o[i*2 + 1];
+            }
+            grd.addColorStop(cValues[i * 4] / 100,'rgba('+ cValues[i * 4 + 1] + ',' + cValues[i * 4 + 2] + ','+cValues[i * 4 + 3] + ',' + opacity + ')');
+        }
+        styleElem.grd = grd;
+    }
+    styleElem.coOp = itemData.o.v*groupTransform.opacity;
+    
+};
+
 CVShapeElement.prototype.renderStroke = function(styleData,itemData, groupTransform){
     var styleElem = itemData.style;
     var d = itemData.d;
diff --git a/player/js/elements/helpers/shapes/SVGGradientFillStyleData.js b/player/js/elements/helpers/shapes/SVGGradientFillStyleData.js
index dd485fb..accd9ba 100644
--- a/player/js/elements/helpers/shapes/SVGGradientFillStyleData.js
+++ b/player/js/elements/helpers/shapes/SVGGradientFillStyleData.js
@@ -10,7 +10,7 @@
     this.e = PropertyFactory.getProp(elem,data.e,1,null,this);
     this.h = PropertyFactory.getProp(elem,data.h||{k:0},0,0.01,this);
     this.a = PropertyFactory.getProp(elem,data.a||{k:0},0,degToRads,this);
-    this.g = new GradientProperty(elem,data.g,this);
+    this.g = new GradientProperty(elem,data.g);
     this.style = styleOb;
     this.stops = [];
     this.setGradientData(styleOb.pElem, data);