Source: uGisAnimation/animation/lineGradientAnimation.js

( function() {
	"use strict";

	/**
	 * lineGradientAnimation 객체.
	 * 
	 * 라인 형태의 피처를 그라데이션 효과를 줄 수 있다.
	 * 
	 * @constructor
	 * 
	 * @example
	 * 
	 * <pre>
	 * var lineGradientAni = new ugmp.animation.lineGradientAnimation( {
	 * 	duration : 5000,
	 * 	repeat : 200,
	 * 	useFade : false
	 * } );
	 * </pre>
	 * 
	 * @param opt_options {Object}
	 * @param opt_options.repeat {Integer} 반복 횟수. Default is `10000`.
	 * @param opt_options.useFade {Boolean} 투명도 효과 사용 여부. Default is `true`.
	 * @param opt_options.duration {Integer} 지연 시간. Default is `2000`.
	 * 
	 * @Extends {ugmp.animation.featureAnimationDefault}
	 * 
	 * @class
	 */
	ugmp.animation.lineGradientAnimation = ( function(opt_options) {
		var _self = this;
		var _super = null;

		this.lineWidth = null;
		this.startColor = null;
		this.endColor = null;
		this.useSymbol = null;
		this.symbolIcon = null;

		this.uGisMap = null;
		this.uGSUtil = null;
		this.dummyContext = null;
		this.symbolSRC = null;
		this.symbolAnchor = null;


		/**
		 * Initialize
		 */
		( function() {

			var options = opt_options || {};

			options.animationType = "lineGradient";

			_super = ugmp.animation.featureAnimationDefault.call( _self, options );

			_self.uGisMap = ( options.uGisMap !== undefined ) ? options.uGisMap : undefined;
			_self.uGSUtil = ugmp.util.uGisGeoSpatialUtil;
			_self.dummyContext = document.createElement( 'canvas' ).getContext( '2d' );

		} )();
		// END Initialize


		return ugmp.util.uGisUtil.objectMerge( _super, {
			_this : _self,
			setStyle : _self.setStyle,
			setUgisMap : _self.setUgisMap
		} );

	} );


	ugmp.animation.lineGradientAnimation.prototype = Object.create( ugmp.animation.featureAnimationDefault.prototype );
	ugmp.animation.lineGradientAnimation.prototype.constructor = ugmp.animation.lineGradientAnimation;


	/**
	 * 애니메이션
	 * 
	 * @override
	 * 
	 * @param e {Object} animateFeature 옵션.
	 * 
	 * @return {Boolean}
	 */
	ugmp.animation.lineGradientAnimation.prototype.animate = function(e) {
		var _self = this._this || this;

		if ( _self.repeat < e.nowNB ) {
			return true;
		}

		var viewExtent = e.frameState.extent;

		// 현재 view 영역에 포함되어 있는 피쳐만 작업.
		if ( ( ol.extent.intersects( viewExtent, e.bbox ) ) ) {
			if ( !( e.time <= _self.duration ) ) {

			}
			_self.customDrawGeom( e, e.geom );
		}

		return ( e.time <= _self.duration );
	};


	/**
	 * 애니메이션 Canvas에 그리기.
	 * 
	 * @param e {Object} animateFeature 옵션.
	 * @param geom {ol.geom.Geometry} 표시할 Geometry.
	 */
	ugmp.animation.lineGradientAnimation.prototype.customDrawGeom = function(e, geom) {
		var _self = this._this || this;

		if ( _self.useFade ) {
			e.context.globalAlpha = ol.easing.easeIn( e.elapsed );
		} else {
			e.context.globalAlpha = 1;
		}

		var vectorContext = e.vectorContext;
		var frameState = e.frameState;

		var cs = e.cs;
		var lens = e.lens;
		var length = e.length;

		var elapsedTime = e.elapsed;
		var len = length * elapsedTime; // 현재 실행 거리
		var fcs = [ cs[ 0 ] ]; // 현재 위치 좌표를 담을 배열 (경로상 임의 위치)
		var idx = 1;
		for ( ; idx < cs.length; idx++ ) {
			var subLen = lens[ idx ];
			if ( subLen >= len ) {
				break;
			} else {
				fcs.push( cs[ idx ] );
			}
		}
		if ( idx < cs.length ) {
			var subLen = lens[ idx ];
			len = subLen - len;
			subLen = subLen - lens[ idx - 1 ];
			var dl = len / subLen;
			var x0 = cs[ idx - 1 ][ 0 ];
			var y0 = cs[ idx - 1 ][ 1 ];
			var x1 = cs[ idx ][ 0 ];
			var y1 = cs[ idx ][ 1 ];
			var c = [ x1 - ( x1 - x0 ) * dl, y1 - ( y1 - y0 ) * dl ];
			fcs.push( c );
		}

		var flashGeo = new ol.geom.LineString( fcs );

		// 경로 그리기
		if ( typeof flashGeo !== "undefined" ) {
			vectorContext.setStyle( _self._gradientStyle( new ol.Feature( {
				geometry : flashGeo
			} ) ) );
			vectorContext.drawGeometry( flashGeo );
		}

		// 현재 위치 심볼 그리기
		_self._createSymbol( vectorContext, flashGeo );
	};


	/**
	 * 라인그라데이션 스타일을 설정한다.
	 * 
	 * style options
	 * 
	 * @param lineWidth {Double} 선 두께.
	 * @param startColor {ol.Color | ol.ColorLike} 그라데이션 색상1.
	 * @param endColor {ol.Color | ol.ColorLike} 그라데이션 색상2.
	 * @param useSymbol {Boolean} 심볼 사용 여부.
	 * @param symbolSRC {String} 심볼 경로 || base64.
	 * @param symbolAnchor {Array.<Double>} 심볼 중심 위치.
	 */
	ugmp.animation.lineGradientAnimation.prototype.setStyle = function(style_) {
		var _self = this._this || this;

		var options = style_ || {};

		if ( options.lineWidth !== undefined ) _self.lineWidth = options.lineWidth;
		if ( options.startColor !== undefined ) _self.startColor = options.startColor;
		if ( options.endColor !== undefined ) _self.endColor = options.endColor;
		if ( options.useSymbol !== undefined ) _self.useSymbol = options.useSymbol;
		if ( options.symbolSRC !== undefined ) {
			var symbolImage = new Image();
			symbolImage.src = options.symbolSRC;

			symbolImage.onload = function() {
				var icon = new ol.style.Icon( {
					img : symbolImage,
					rotation : 0,
					rotateWithView : true,
					imgSize : [ this.width, this.height ],
					anchor : options.symbolAnchor
				} );

				_self.symbolIcon = new ol.style.Style( {
					image : icon
				} );
			}
		}

		_self.style = new ol.style.Style( {
			stroke : new ol.style.Stroke( {
				color : _self.startColor,
				width : _self.lineWidth
			} )
		} );
	};


	/**
	 * 그라데이션 설정
	 * 
	 * @private
	 * 
	 * @param feature {ol.Feature} 대상 피쳐
	 */
	ugmp.animation.lineGradientAnimation.prototype._gradientStyle = function(feature_) {
		var _self = this._this || this;

		var feature = feature_;
		var pixelStart;
		var pixelEnd;
		var extent = feature.getGeometry().getExtent();
		var startP = feature.getGeometry().getFirstCoordinate();
		var centerP = ol.extent.getCenter( extent );

		var angle = _self.uGSUtil.getDegreeBtwPoints( startP, centerP );

		if ( ( 0 <= angle && angle < 90 ) || ( -90 < angle && angle < 0 ) ) {
			// TopLeft -> TopRight
			pixelStart = ol.extent.getTopLeft( extent );
			pixelEnd = ol.extent.getTopRight( extent );
		} else if ( 90 === angle ) {
			// BottomRight -> TopRight
			pixelStart = ol.extent.getBottomRight( extent );
			pixelEnd = ol.extent.getTopRight( extent );
		} else if ( ( 90 < angle && angle < 180 ) || ( 180 === angle ) || ( -180 < angle && angle < -90 ) ) {
			// TopRight -> TopLeft
			pixelStart = ol.extent.getTopRight( extent );
			pixelEnd = ol.extent.getTopLeft( extent );
		} else if ( -90 === angle ) {
			// TopRight -> BottomRight
			pixelStart = ol.extent.getTopRight( extent );
			pixelEnd = ol.extent.getBottomRight( extent );
		}

		var left = _self.uGisMap.getMap().getPixelFromCoordinate( pixelStart );
		var right = _self.uGisMap.getMap().getPixelFromCoordinate( pixelEnd );

		var grad = _self.dummyContext.createLinearGradient( left[ 0 ], left[ 1 ], right[ 0 ], right[ 1 ] );

		grad.addColorStop( 0, _self.startColor );
		grad.addColorStop( 1, _self.endColor );

		var style = _self.style;

		style.getStroke().setColor( grad );

		return style;
	};


	/**
	 * 현재 위치에 심볼 그리기
	 * 
	 * @private
	 * 
	 * @param vectorContext {ol.render.VectorContext} vectorContext
	 * @param geometry {ol.geom.LineString | ol.geom.MultiLineString} 애니메이션 대상 피쳐
	 */
	ugmp.animation.lineGradientAnimation.prototype._createSymbol = function(vectorContext_, geometry_) {
		var _self = this._this || this;

		if ( !vectorContext_ || !geometry_ ) {
			return;
		}
		var coords = geometry_.getCoordinates();
		var startP = coords[ coords.length - 2 ];
		var endP = coords[ coords.length - 1 ];

		var currentPoint = new ol.geom.Point( endP ); // 현재 위치를 나타낼 포인트

		// 현재 위치 포인트 그리기
		if ( typeof currentPoint !== "undefined" ) {
			if ( _self.useSymbol && _self.symbolIcon ) {
				var rotation = _self.uGSUtil.getRadianBtwPoints( startP, endP );
				_self.symbolIcon.getImage().setRotation( -rotation );
				vectorContext_.setStyle( _self.symbolIcon );
			}

			vectorContext_.drawGeometry( currentPoint );
		}
	};


	/**
	 * 현재 설정된 속성 정보를 가져온다.
	 * 
	 * @override {ugmp.animation.featureAnimationDefault.prototype.getProperties}
	 * 
	 * @return {Object} 현재 설정된 속성 정보.
	 */
	ugmp.animation.lineGradientAnimation.prototype.getProperties = function() {
		var _self = this._this || this;

		var superProperties = ugmp.animation.featureAnimationDefault.prototype.getProperties.call( this );

		return ugmp.util.uGisUtil.objectMerge( superProperties, {

		} );
	};


	/**
	 * uGisMap을 설정한다.
	 * 
	 * @param uGisMap {ugmp.uGisMap} {@link ugmp.uGisMap ugmp.uGisMap} 객체.
	 */
	ugmp.animation.lineGradientAnimation.prototype.setUgisMap = function(uGisMap_) {
		var _self = this._this || this;

		_self.uGisMap = uGisMap_;
	};

} )();