board/annotation.js

ModuleBase.define("Annotation", ["RestServer", "Error", "TextInfo"], function(RestServer,Error,TextInfo) {
	
	/**
     * @desc Annotation构造函数。
     * @constructor
     * @alias Annotation
     */
	var Annotation = function(boardId, roomHandle, board) {
		this.roomHandle = roomHandle;
		this.boardId = boardId; //白板ID
		this.shapeType = shapeTypeEnum.mouse;  //几何形状 or 操作类型
		this.color = 'rgba(0,0,0,1)'; //注释设置颜色,默认值为黑色
        this.backgroundColor =  '';
		this.lineWidth = 2; //线条宽度,默认值为2
		this.arrowLength = 10; //设置箭头宽度,默认值为10
		this.arrowtype = arrowTypeEnum.end; //箭头类别
		this.fillType = fillTypeEnum.none;//是否需要填充
		this.fillColor = 'none'; //填充颜色	
        this.initType = AnnotationInitTypeEnum.full; // 批注层默认填充类型为平铺,铺满整个外部容器
		
		this.shapeArray = []; //批注图形数组
		this.redoArray = [];
		this.undoArray = []; // 撤销图形数组,用于撤销时恢复记录的操作
        this.zr = null; //zrender对象实例
		this.annoDiv = null; // 用户传入的白板外部容器div
        this.renderDiv = null; // 计算得到的内部使用的白板容器
        this.renderBackground = null; // 和renderDiv同样大小的背景层,用于控制背景色或背景图
		
		this.currentIndex = -1 ;
		
		this.drawOrEraser = annoSelectTypeEnum.draw; //注释类型
		
		this.isEraser = false; //橡皮擦按下状态
		this.eraserShape = null;
		this.eraserImageWidth = 32;
        this.eraserImageHeight = 32;

		this.otherShapeMap = {}; //其他人
		this.screenSize = {
			width:0,
			height:0
		};
		this.hlightPointInfo = {
			imgUrl:'',
			width:'',
			height:'',
			intervalTime: 200,
			isSend:true
		}
		this.hlightPointShape = null; //TODO 有时间重新整理逻辑    使用map  {userId:hlightPointShape} 可以有多个激光笔
		this.textTagShape = null;


		// 移动端属性
		this.arrowLineStartPoint = {  //定义arrowline的起始坐标,防止点击不拖动时绘制的问题
			x: 0,
			y: 0
		}

		this.currentX = 0;
        this.currentY = 0;
		this.x = 0;
		this.y = 0;
		this.shapeData = [];
		this.rhombData = [];
		this.shapeObj = {};
		this.shape = null;
		
		this.imgZlevel = -99999;
        this.normalZlevel = 10000;
        this.tempHighZlevel = 99999;

		this.currActiveImg = null;
		this.isAllowEditImg = false;
        
		// 防止图片跳动
        this.dragData = {
            drag: false,
            pos: [0,0]
        }

        this.shapeGroup = new zrender.Group({
            slient: false // 组内子孙元素是否响应鼠标事件
        });
        
        this.textInfo = null;
        // 缩放的位置信息
        this.positionInfo = {
            scaleWidth: 0, // 缩放目标宽度
            scaleHeight: 0, // 缩放目标高度
            zrWidth: 0, // 当前画布的宽度
            zrHeight: 0, // 当前画布的高度
            scaleRatioX: 1, // 横向缩放比例,缩放目标宽度和当前画布宽度的比值
            scaleRatioY: 1, // 纵向缩放比例,缩放目标高度和当前画布高度的比值
            scaleRatioXR: 1, // 横向缩放比例,当前画布宽度和缩放目标宽度的比值
            scaleRatioYR: 1, // 纵向缩放比例,当前画布宽度和缩放目标宽度的比值,
            originalWidth: 0, // 初始的宽度
            originalHeight: 0 // 初始的高度
        };
        
        // 图片缩放后鼠标捕获的坐标与图片左上角坐标的偏移量
        this.imgScaleoffset = {
            x: 0,
            y: 0
        };

        this.board = board;

        this.backgroundColor = board.backgroundColor;
	};

	var Successright = zrender.Path.extend({
        type: 'Successright',
        shape: {
            // x, y on the cusp
            x:0,
            y:0,
            width:0,
            height:0
            
        },
        buildPath: function (path, shape) {
            var x1 = shape.x;
            var y1 = shape.y + shape.height/3*2;
            var x2 = shape.x + shape.width/2;
            var y2 = shape.y + shape.height;
            var x3 = shape.x + shape.width;
            var y3 = shape.y;
            path.moveTo(x1,y1);
            path.lineTo(x2,y2);
            path.lineTo(x3,y3);
        }
    });
	
	
	var Failure = zrender.Path.extend({
        type: 'Failure',
        shape: {
            // x, y on the cusp
            x: 0,
            y: 0,
            width:0,
            height:0
        },
        buildPath: function (path, shape) {
            var x = shape.x;
            var y = shape.y;
            var width = shape.width;
            var height = shape.height;
            var leftX = x - width/2;
            var rightX = x + width/2;
            var topY = y - height/2;
            var bottomY = y + height/2;
            path.moveTo(leftX,topY);
            path.lineTo(rightX,bottomY);
            path.closePath();
            path.moveTo(rightX,topY);
            path.lineTo(leftX,bottomY);
            path.closePath();
        }
    });
    
	var Arrow = zrender.Path.extend({
        type: 'Arrow',
        shape: {
            // x, y on the cusp
            x: 0,
            y: 0,
            width:0,
            height:0
        },
        buildPath: function (path, shape) {
            var x1 = shape.x;
            var y1 = shape.y + shape.height/3;
            var x2 = shape.x;
            var y2 = shape.y + shape.height/3 * 2;
            var x3 = shape.x + shape.width/3 * 2;
            var y3 = shape.y + shape.height/3 * 2;
            var x4 = shape.x + shape.width/3 * 2;
            var y4 = shape.y + shape.height;
            var x5 = shape.x + shape.width;
            var y5 = shape.y + shape.height/2;
            var x6 = shape.x + shape.width/3 * 2;
            var y6 = shape.y;
            var x7 = shape.x + shape.width/3 * 2;
            var y7 = shape.y + shape.height/3;
            path.moveTo(x1,y1);
            path.lineTo(x2,y2);
            path.lineTo(x3,y3);
            path.lineTo(x4,y4);
            path.lineTo(x5,y5);
            path.lineTo(x6,y6);
            path.lineTo(x7,y7);
            path.closePath();
        }
    }); 
    
	var Texttag = zrender.Path.extend({
        type: 'Texttag',
        shape: {
            // x, y on the cusp
            x: 0,
            y: 0,
            width:0,
            height:0
        },
        buildPath: function (path, shape) {
            var x1 = shape.x;
            var y1 = shape.y + shape.height/4;
            var x2 = shape.x;
            var y2 = shape.y + shape.height/4 * 3;
            var x3 = shape.x + shape.width/5 * 4;
            var y3 = shape.y + shape.height/4 * 3;
            var x4 = shape.x + shape.width/5 * 4;
            var y4 = shape.y + shape.height;
            var x5 = shape.x + shape.width;
            var y5 = shape.y + shape.height/2;
            var x6 = shape.x + shape.width/5 * 4;
            var y6 = shape.y;
            var x7 = shape.x + shape.width/5 * 4;
            var y7 = shape.y + shape.height/4;
            path.moveTo(x1,y1);
            path.lineTo(x2,y2);
            path.lineTo(x3,y3);
            path.lineTo(x4,y4);
            path.lineTo(x5,y5);
            path.lineTo(x6,y6);
            path.lineTo(x7,y7);
            path.closePath();
        }
    });

    /**
     * @description 通过外部盒子和内容盒子的宽高,算出内容盒子撑满外部盒子的宽或者高的结果
     * @param {int} outWidth 外部容器宽
     * @param {int} outHeight 外部容器高
     * @param {int} contentWidth 内容的宽
     * @param {int} contentHeight 内容的高
     */
	function getCompatibleSize(outWidth, outHeight, contentWidth, contentHeight) {
        var renderWidth = 0;
        var renderHeight = 0;
        // 白板的宽高比
        var contentRatio = contentWidth / contentHeight;
        // 外部容器的宽高比
        var outRatio = outWidth / outHeight;
    
        if(contentRatio > 1 && outRatio > 1){
            // 说明都是横向
            if(contentRatio > outRatio){
                // 说明白板比容器比例更宽
                // 白板的宽度要撑满容器宽度、上下留白
                renderWidth = outWidth;
                if(contentWidth > outWidth){
                    // 白板更宽
                    renderHeight = contentHeight / (contentWidth / outWidth)
                }else{
                    // 容器更宽,撑满容器后,高也要增加
                    renderHeight = contentHeight * (outWidth / contentWidth)
                }
            }else if(contentRatio < outRatio){
                // 说明白板比容器比例更窄
                // 白板的宽度要撑满容器高度、左右留白
                renderHeight = outHeight;
                if(contentHeight > outHeight){
                    // 白板更高
                    renderWidth = contentWidth / (contentHeight / outHeight)
                }else{
                    // 容器更高
                    renderWidth = contentWidth * (outHeight / contentHeight)
                }
            }else{
                // 比例相等,那直接撑满容器宽高
                renderWidth = outWidth
                renderHeight = outHeight
            }
            // 都要设置白板渲染比例
        }else if(contentRatio > 1 && outRatio <= 1){
            // 白板横向,容器正方形或竖向的时候
            // 白板的宽度要撑满容器宽度、上下留白
            renderWidth = outWidth;
            if(contentWidth > outWidth){
                // 白板更宽
                renderHeight = contentHeight / (contentWidth / outWidth)
            }else{
                // 容器更宽,撑满容器后,高也要增加
                renderHeight = contentHeight * (outWidth / contentWidth)
            }
        }else if(contentRatio < 1 && outRatio > 1){
            // 白板竖向,容器横向
            // 白板的宽度要撑满容器高度、左右留白
            renderHeight = outHeight;
            if(contentHeight > outHeight){
                // 白板更高
                renderWidth = contentWidth / (contentHeight / outHeight)
            }else{
                // 容器更高
                renderWidth = contentWidth * (outHeight / contentHeight)
            }
        }else if(contentRatio < 1 && outRatio <= 1){
            // 白板竖向,容器正方形或者竖向
            if(contentRatio < outRatio){
                // 白板更窄,撑满容器高度,左右留白
                renderHeight = outHeight;
                if(contentHeight > outHeight){
                    // 白板更高
                    renderWidth = contentWidth / (contentHeight / outHeight)
                }else{
                    // 容器更高
                    renderWidth = contentWidth * (outHeight / contentHeight)
                }
            }else{
                // 容器更窄,撑满容器宽度,上下留白
                renderWidth = outWidth;
                if(contentWidth > outWidth){
                    // 白板更宽
                    renderHeight = contentHeight / (contentWidth / outWidth)
                }else{
                    // 容器更宽,撑满容器后,高也要增加
                    renderHeight = contentHeight * (outWidth / contentWidth)
                }
            }
        }else{
            if(contentRatio == 1){
                if(outRatio > 1){
                    // 直接撑满高,左右留白
                    renderHeight = outHeight;
                    if(contentHeight > outHeight){
                        // 白板更高
                        renderWidth = contentWidth / (contentHeight / outHeight)
                    }else{
                        // 容器更高
                        renderWidth = contentWidth * (outHeight / contentHeight)
                    }
                }else{
                    // 直接撑满宽,上下留白
                    renderWidth = outWidth;
                    if(contentWidth > outWidth){
                        // 白板更宽
                        renderHeight = contentHeight / (contentWidth / outWidth)
                    }else{
                        // 容器更宽,撑满容器后,高也要增加
                        renderHeight = contentHeight * (outWidth / contentWidth)
                    }
                }
            }
        }
        log.debug('===annotation getCompatibleSize, outWidht: ' + outWidth + ',outHeight: ' + outHeight + ',contentWidth: ' + contentWidth + ',contentHeight: ' + contentHeight + ',renderWidth: ' + renderWidth + ',renderHeight: ' + renderHeight);
        return [renderWidth, renderHeight];
    }

    Annotation.prototype.initAndGetRenderCanvasByBoardAndWrapBox = function() {
        var self = this;
        var renderCanvas = document.createElement('canvas');
        var renderCanvasWidth = 0;
        var renderCanvasHeight = 0;
        var compatibleSize = getCompatibleSize(self.annoDiv.clientWidth, self.annoDiv.clientHeight, self.board.width, self.board.height);

        renderCanvasWidth = compatibleSize[0];
        renderCanvasHeight = compatibleSize[1];
        renderCanvas.width = renderCanvasWidth;
        renderCanvas.height = renderCanvasHeight;

        renderCanvas.style.width = renderCanvasWidth + 'px';
        renderCanvas.style.height = renderCanvasHeight + 'px';
        renderCanvas.style.background = 'rgba(0,0,0,0)';
        self.board.setRenderWidthAndHeight(renderCanvasWidth, renderCanvasHeight);
        log.debug("===annotation initAndGetRenderCanvasByBoardAndWrapBox, result: bw: " + self.board.width + ',bh: ' + self.board.height + ",ww: " + self.annoDiv.clientWidth + ",wh: " + self.annoDiv.clientHeight + ",rw: " + renderCanvasWidth + ",rh: " + renderCanvasHeight);
        return renderCanvas;
    }

    /**
     * @ignore
     */
    Annotation.prototype.updateBackgroundColor = function (color) {
        if(!this.renderBackground){
            return;
        }
        if(!this.renderBackground instanceof HTMLCanvasElement){
            return;
        }
        var ctx = this.renderBackground.getContext('2d');
        if(color){
            ctx.fillStyle = 'rgba(' + color + ')';
            ctx.fillRect(0, 0, this.renderBackground.width, this.renderBackground.height);
            this.backgroundColor = color;
        }else{
            ctx.clearRect(0, 0, this.renderBackground.width, this.renderBackground.height);
        }
    }

    /**
     * @ignore
     */
    Annotation.prototype.updateBackgroundImage = function (imageUrl) {
		var deferred = when.defer();
        log.debug('===annotation updateBackgroundImage, imageUrl:'+ imageUrl);
        var self = this;
        if(!imageUrl){
            deferred.reject('===annotation updateBackgroundImage error, reason: imageUrl is invalid!');
        }
        if(!this.renderBackground){
            deferred.reject('===annotation updateBackgroundImage error, reason: board background is not exist!');
        }
        if(!this.renderBackground instanceof HTMLCanvasElement){
            deferred.reject('===annotation updateBackgroundImage error, reason: board background element is not canvas');
        }

        try{
            this.updateBackgroundColor(this.backgroundColor);

            var ctx = this.renderBackground.getContext('2d');
            var tempImageEl = document.createElement('img');
            tempImageEl.src = imageUrl;
            tempImageEl.onload = function(){
                var compatibleSize = getCompatibleSize(self.renderBackground.width, self.renderBackground.height, tempImageEl.width, tempImageEl.height);
                var startDrawX = (self.renderBackground.width / 2) - (compatibleSize[0] / 2);
                var startDrawY = (self.renderBackground.height / 2) - (compatibleSize[1] / 2);
                log.debug("===annotation updateBackgroundImage, startX:" + startDrawX + ",startY:" + startDrawY + ",compatibleSize:" + compatibleSize);
                ctx.drawImage(tempImageEl, startDrawX, startDrawY, compatibleSize[0], compatibleSize[1]);
                deferred.resolve();
            }
        }catch(err){
            log.debug("===annotation updateBackgroundImage, err:" + err);
            deferred.reject(err);
        }
        return deferred.promise; 
    }
	
	/**
     * @desc 初始化annotation对象
     * @param {HTMLDivElement} annoDiv - 注释所在的应用层容器,只能传入div元素 
	 * @param {String} annotationInitType - 注释初始化类型 可选值: AnnotationInitTypeEnum.proportional, AnnotationInitTypeEnum.full
     */	
	Annotation.prototype.init = function(annoDiv, annotationInitType){
		var deferred = when.defer();
		var self = this;

        
        if(!(annoDiv instanceof HTMLDivElement) && !(annoDiv instanceof HTMLCanvasElement)){
            log.error('===annotation init faild, must be given a div or canvas element!');
            deferred.reject('Must be given a div or canvas element!');
            return deferred.promise;
        }
        
        self.annoDiv = annoDiv;

        if(annotationInitType && annotationInitType == AnnotationInitTypeEnum.proportional){
            self.initType = annotationInitType;

            // 如果初始化类型是proportional(成比例)则需要给定一个div元素,以便在此div内部创建批注和背景层
            if(!(annoDiv instanceof HTMLDivElement)){
                log.error('===annotation init faild, proportional type must be given a div element!')
                deferred.reject('Proportional type must be given a div element!');
                return deferred.promise;
            }

            annoDiv.style.display = 'flex';
            annoDiv.style.alignItems = 'center';
            annoDiv.style.justifyContent = 'center';

            // 初始化canvas相关
            this.renderDiv = self.initAndGetRenderCanvasByBoardAndWrapBox();
            this.renderBackground = self.initAndGetRenderCanvasByBoardAndWrapBox();
            this.renderDiv.style.position = 'relative';
            this.renderDiv.style.zIndex = 1001;
            annoDiv.appendChild(this.renderDiv);
            
            // 背景层处理,用于设置背景色或者背景图
            this.renderBackground.style.position = 'absolute';
            // TODO 暂时注释,在佳会中设置下面属性,会导致打开文字功能时,背景层canvas会移位
            // this.renderBackground.style.left = this.renderDiv.offsetLeft + 'px';
            // this.renderBackground.style.top = this.renderDiv.offsetTop + 'px';
            this.renderBackground.style.zIndex = 1000;
            this.renderBackground.className = 'annotation-canvas-background';

            annoDiv.appendChild(this.renderBackground);
        }else{
            this.renderDiv = annoDiv;
        }
        

        

		self.zr = zrender.init(this.renderDiv);
		self.zr.add(self.shapeGroup);
        self.positionInfo.originalWidth = self.zr.getWidth();
        self.positionInfo.originalHeight = self.zr.getHeight();
        
		
		//因为self.annoDiv.style.width及self.annoDiv.style.height 获取到的是100%,所以取第一个子节点的宽及高
		if(self.annoDiv.style.width == "100%"){
			var childDiv= self.annoDiv.firstChild;
			self.screenSize.width = parseInt(childDiv.style.width);
		    self.screenSize.height = parseInt(childDiv.style.height);
		    log.debug("===Annotation.init(),childDiv.style.width:"+childDiv.style.width+",childDiv.style.height:"+ childDiv.style.height);
		}else{
			self.screenSize.width = parseInt(self.annoDiv.style.width);
		    self.screenSize.height = parseInt(self.annoDiv.style.height);
		    log.debug("===Annotation.init(),annoDiv.style.width:"+annoDiv.style.width+",annoDiv.style.height:"+ annoDiv.style.height);
		}

        this.updateBackgroundColor(this.backgroundColor);

        // 图片拖拽
        self.zr.on('mousedown', function(e) {
            if(!self.isAllowEditImg){
                return;
            }

            // 画布拖拽的起始位置是 事件对象中的画布的坐标位置
            self.dragData.pos = [e.event.zrX, e.event.zrY];
			
            // 画布的拖拽目标
            if(e.target && e.target.type && e.target.type == 'image' && e.target.draggable != null){
				
                
                // 设置当前激活的图片
                self.currActiveImg = e.target;
				self.currActiveImg.attr('zlevel', self.tempHighZlevel);

                
                // 取图片的父节点,为shapeGroup
                var shapeGroup = self.currActiveImg.parent;
				
                // 给当前激活的图片添加高亮边框,其他图片取消高亮边框
				shapeGroup.eachChild(function(item){
					if(item.type === 'image'){
                        if(item.id == self.currActiveImg.id){
                            item.attr('style',{shadowColor:'red',shadowBlur:5});
                        }else{
                            item.attr('style',{shadowColor:undefined,shadowBlur:undefined});
                        }
                    }
				})

                if(self.currActiveImg.scale[0] == 1){
                    self.imgScaleoffset.x = 0;
                    self.imgScaleoffset.y = 0;
                }else{
                    var scale = self.currActiveImg.scale[0] - 1;
                    self.imgScaleoffset.x = self.currActiveImg.getWidth() * scale / 2;
                    self.imgScaleoffset.y = self.currActiveImg.getHeight() * scale / 2;
                }
            }

            if (e.target === undefined) {
                self.dragData.drag = false
            } else {
                self.dragData.drag = true
            }
        })

        self.zr.on('mouseup', function(e) {
            if(!self.isAllowEditImg){
                return;
            }
            
            if(!self.currActiveImg){
                return;
            }

            self.dragData.drag = false;
			
            self.currActiveImg.customPositionX = self.currActiveImg.position[0] - self.imgScaleoffset.x;
            self.currActiveImg.customPositionY = self.currActiveImg.position[1] - self.imgScaleoffset.y;
            
            log.debug('===Annotation.drawImg, currActiveImg customPosition: ',[self.currActiveImg.customPositionX, self.currActiveImg.customPositionY].toString())

            self.currActiveImg.attr('zlevel', ++self.imgZlevel);

            self.roomHandle.sendUpdateAnnotation(self.boardId, self.currActiveImg, shapeTypeEnum.img);
        })

        self.zr.on('mouseout', function(e) {
            if(!self.isAllowEditImg){
                return;
            }
            self.dragData.drag = false;
        })

        self.zr.on('mousemove', function(e) {
            if (self.dragData.drag === false) {
                if(self.currActiveImg){
                    self.currActiveImg.draggable = false;
                }
                return;
            }
    
            if (self.currActiveImg != null && self.isAllowEditImg === true) {
				var new_pos = [e.event.zrX, e.event.zrY]
                var pos = [
                    new_pos[0] - self.dragData.pos[0],
                    new_pos[1] - self.dragData.pos[1]
                ]
				
				//图片拖动中,区域位置不能移出白板之外,放大缩小图片及重设置白板宽高后还有效。 by roymond 2024/11/7
				var xMin = self.currActiveImg.position[0] + pos[0] * self.positionInfo.scaleRatioXR - self.currActiveImg.style.width * (self.currActiveImg.scaleX -1)/2;
				var yMin = self.currActiveImg.position[1] + pos[1] * self.positionInfo.scaleRatioYR - self.currActiveImg.style.height * (self.currActiveImg.scaleY -1)/2;
				
				var xMax = xMin + self.currActiveImg.style.width  * self.currActiveImg.scaleX;
				var yMax = yMin + self.currActiveImg.style.height * self.currActiveImg.scaleY;
				
				if( xMin <0 ||  yMin<0 || xMax> self.board.width || yMax> self.board.height){
					return;
				}
			    //end 
				
                self.currActiveImg.position[0] += pos[0] * self.positionInfo.scaleRatioXR;
                self.currActiveImg.position[1] += pos[1] * self.positionInfo.scaleRatioYR;
				
				//console.log("===RRRRRRRRR222222222,"+ self.currActiveImg.position[0] +" , " + self.currActiveImg.position[1]);
				
                self.currActiveImg.dirty();
				self.dragData.pos = new_pos;
            } 
        })
		
		self.renderDiv.onmousedown = function(e){

			if(self.isAllowEditImg){
				return;
			}
			if(self.shapeType == shapeTypeEnum.mouse || self.shapeType == shapeTypeEnum.hlight_point){
				return;
			}
			
			if(self.shapeType == shapeTypeEnum.eraser){
				self.isEraser = true;
			}
			var shapeObj = {};
            
			var x,y;
            /**
             * 之前用的是e.clientX - this.getBoundingClientRect().left这种来计算点击坐标
             * 其实可以直接使用e.offsetX来
             */
            var tempPos = self.handleScaledXY(e.clientX - this.getBoundingClientRect().left, e.clientY - this.getBoundingClientRect().top);
            self.currentX = tempPos.x;
			self.currentY = tempPos.y;
			
			shapeData = [];
			shapeData.push([self.currentX, self.currentY]);
			var shape = null;
			switch (self.shapeType){
				
				
				case shapeTypeEnum.failure:
					shape = new Failure({
						shape:{
							x:self.currentX,
							y:self.currentY,
							width:20,
							height:20
						},
						style: {
							stroke: self.color,
							lineWidth:self.lineWidth
						},
                        zlevel: self.normalZlevel,
						onmouseover:mouseOverForEraser
					})
					break;
				
				case shapeTypeEnum.success:
					shape = new Successright({
						shape:{
							x:self.currentX - 10,
							y:self.currentY - 10,
							width:20,
							height:20
						},
						style: {
							stroke: self.color,
							fill: 'none',
							lineWidth:self.lineWidth
						},
						onmouseover:mouseOverForEraser
					})
					break;
				
				
				
				case shapeTypeEnum.arrow:
					shape = new Arrow({
						shape:{
							x:self.currentX - 15,
							y:self.currentY - 10,
							width:30,
							height:20
						},
						style: {
							stroke: self.color,
							fill: self.fillColor,
							lineWidth:self.lineWidth
						},
                        zlevel: self.normalZlevel,
						onmouseover:mouseOverForEraser
					})
					break;
					
				case shapeTypeEnum.hlight_texttag:
                    var userName = self.roomHandle.selfUser.name;
                    var renderName = userName;
                    if(userName.split('').length > 7){
                        renderName = userName.substr(0, 7) + '...';
                    }
					if(self.textTagShape){
						self.textTagShape.attr({
							shape: {
								x:self.currentX - 40,
								y:self.currentY - 20
							},
							style:{
								text: renderName
							},
                            zlevel: self.normalZlevel,
                            textContent: new zrender.Text({
                                x: self.currentX - 40 + 5,
                                y: self.currentY - 20 + 13,
                                style: {
                                    text: renderName,
                                    fill: '#ffffff',
                                    textRect: {
                                        x: self.currentX - 40 + 5,
                                        y: self.currentY - 20 + 13,
                                        width: 50
                                    },
                                    truncate: {
                                        outerWidth: 50
                                    }
                                },
                                zlevel: self.normalZlevel
                            })
						});
						self.roomHandle.sendAddAnnotation(self.boardId, self.textTagShape, self.shapeType, self.color, self.lineWidth, self.arrowtype, self.fillType);
					}else{
						self.textTagShape = new Texttag({
							shape:{
								x:self.currentX - 40,
								y:self.currentY - 20,
								width:80,
								height:40
							},
							style: {
								stroke: "red",
								fill: "red",
								text: renderName,
								truncate:{
									outerWidth:50
								},
								lineWidth:self.lineWidth
							},
                            zlevel: self.normalZlevel,
                            textContent: new zrender.Text({
                                x: self.currentX - 40 + 5,
                                y: self.currentY - 20 + 13,
                                style: {
                                    text: renderName,
                                    fill: '#ffffff',
                                    textRect: {
                                        x: self.currentX - 40 + 5,
                                        y: self.currentY - 20 + 13,
                                        width: 50
                                    },
                                    truncate: {
                                        outerWidth: 50
                                    }
                                },
                                zlevel: self.normalZlevel
                            }),
							onmouseover:mouseOverForEraser
						})
						self.textTagShape.id = Math.uuid();
                        self.shapeGroup.add(self.textTagShape);
						self.roomHandle.sendAddAnnotation(self.boardId, self.textTagShape, self.shapeType, self.color, self.lineWidth, self.arrowtype, self.fillType);
					}
					break;
				
				case shapeTypeEnum.rhomb:
					shape = new zrender.Polygon({
						shape:{
							points: [],
							smooth:0,
						},
						style: {
							stroke: self.color,
							fill: 'none',
							lineWidth:self.lineWidth
						},
                        zlevel: self.normalZlevel,
						onmouseover:mouseOverForEraser
					})
					break;
					
				case shapeTypeEnum.line:
					shape = new zrender.Arrowline({
						shape:{
							x1: self.currentX,
							y1: self.currentY,
							x2: self.currentX,
							y2: self.currentY,
							arrowlength: 10,
							arrowtype: self.arrowtype
						},
						style: {
							stroke: self.color,
							lineWidth:self.lineWidth
						},
                        zlevel: self.normalZlevel,
						onmouseover:mouseOverForEraser
					})
					break;
				
					
				case shapeTypeEnum.polyline:
					shape = new zrender.Polyline({
						shape:{
							points: [],
							smooth:0,
						},
						style: {
							stroke: self.color,
							lineWidth:self.lineWidth
						},
                        zlevel: self.normalZlevel,
						onmouseover:mouseOverForEraser
					})
					break;
					
				case shapeTypeEnum.rect:
					shape = new zrender.Rect({
						shape:{
							x:self.currentX,
							y:self.currentY
						},
						style: {
							stroke: self.color,
							fill: self.fillColor,
							lineWidth:self.lineWidth
						},
                        zlevel: self.normalZlevel,
						onmouseover:mouseOverForEraser
					})
					break;
				
				case shapeTypeEnum.ellipse:
					
					shape = new zrender.Ellipse({
						
						shape:{
							cx:self.currentX,
							cy:self.currentY,
							rx:0,
							ry:0,
						},
						style: {
							stroke: self.color,
							fill: self.fillColor,
							lineWidth:self.lineWidth
						},
                        zlevel: self.normalZlevel,
						onmouseover:mouseOverForEraser
					})
					break;
				
				default:
					break;
			}
			if(shape){
				shape.id = Math.uuid();
                self.shapeGroup.add(shape);
			}else{
				return;
			}
			
			self.renderDiv.onmousemove = function(e){
				if(self.isAllowEditImg){
					return;
				}
				
				if(self.shapeType == shapeTypeEnum.eraser){
					return;
				}
                var tempPos = self.handleScaledXY(e.clientX - this.getBoundingClientRect().left, e.clientY - this.getBoundingClientRect().top);
				x = tempPos.x;
				y = tempPos.y;
				shapeData.push([x, y]);
				var rhombData = [[self.currentX, self.currentY + (y-self.currentY)/2],[self.currentX + (x - self.currentX)/2, y],[x, self.currentY + (y-self.currentY)/2],[self.currentX + (x - self.currentX)/2, self.currentY]]
				switch (self.shapeType){
					
					case shapeTypeEnum.rhomb:
						shape.attr('shape',{
							points: rhombData
						})
						break;
						
					case shapeTypeEnum.line:
						shape.attr('shape',{
							x2:x,
							y2:y
						})
						break;
						
					case shapeTypeEnum.polyline:
						shape.attr('shape',{
							points: shapeData
						})
						break;
						
					case shapeTypeEnum.rect:
						shape.attr('shape',{
							width:x-self.currentX,
							height:y-self.currentY
						})
						break;
						
					case shapeTypeEnum.ellipse:
						shape.attr('shape',{
							cx:x + (self.currentX-x)/2,
							cy:y + (self.currentY-y)/2,
							rx:Math.abs(x-self.currentX)/2,
							ry:Math.abs(y-self.currentY)/2,
						})
						break;
						
					default:
						break;
				}
			}
			
			self.renderDiv.onmouseup = function(){
				self.renderDiv.onmousemove = null;
				self.renderDiv.onmouseup = null;
				self.renderDiv.onmouseleave = null;
				
				if(self.isAllowEditImg){
					return;
				}
				
				if(self.shapeType == shapeTypeEnum.eraser){
					self.isEraser = false;
					return;
				}
				var shapeObj = {
					shape:shape,
					shapeType:self.shapeType,
					color:self.color,
					lineWidth:self.lineWidth,
					arrowtype:self.arrowtype,
					fillType:self.fillType,
				}
				self.shapeArray.push(shapeObj);
				self.undoArray.push({
					type: 'add',
					shapeObj: shapeObj
				})
				self.redoArray = [];
				self.roomHandle.sendAddAnnotation(self.boardId, shape, self.shapeType, self.color, self.lineWidth, self.arrowtype, self.fillType);
			}
			
			self.renderDiv.onmouseleave = function(){
				self.renderDiv.onmousemove = null;
				self.renderDiv.onmouseup = null;
				self.renderDiv.onmouseleave = null;
				
				if(self.isAllowEditImg){
					return;
				}
				
				if(self.shapeType == shapeTypeEnum.eraser){
					self.isEraser = false;
					return;
				}
				var preWidth = parseInt(self.renderDiv.style.width);
				var preHeight = parseInt(self.renderDiv.style.height);
				self.shapeArray.push({
					shape:shape,
					shapeType:self.shapeType,
					color:self.color,
					lineWidth:self.lineWidth,
					arrowtype:self.arrowtype,
					fillType:self.fillType,
				});
				self.redoArray = [];
				self.roomHandle.sendAddAnnotation(self.boardId, shape, self.shapeType, self.color, self.lineWidth, self.arrowtype, self.fillType);
			}
		}
		function mouseOverForEraser(params) {
			if(self.shapeType == shapeTypeEnum.eraser && self.isEraser){
				for (var i = 0; i < self.shapeArray.length; i++) {
					var shapeObj = self.shapeArray[i];
					if(shapeObj.shape.id == params.target.id){
						self.zr.remove(shapeObj.shape)
                        self.shapeGroup.remove(shapeObj.shape)
						self.roomHandle.sendRemoveAnnotation(self.boardId,self.shapeType,shapeObj.shape.id)
						self.undoArray.push({
							type: 'delete',
							shapeObj: shapeObj
						})
						self.shapeArray.splice(i,1);
					}
				}
			}
		}



		self.renderDiv.ontouchstart = function(e){
			if(self.isAllowEditImg){
				return;
			}
			
			e.preventDefault(); // 阻止触摸默认滚动页面事件,防止页面滚动

			if(self.shapeType == shapeTypeEnum.mouse || self.shapeType == shapeTypeEnum.hlight_point){
				return;
			}
			
			if(self.shapeType == shapeTypeEnum.eraser){
				self.isEraser = true;
				return;
			}

			//使用更精确的坐标计算
			var rect = this.getBoundingClientRect();
			var touch = e.touches[0];
			var rawX = touch.clientX - rect.left;
			var rawY = touch.clientY - rect.top;
			
			var tempPos = self.handleScaledXY(rawX, rawY);
			self.currentX = tempPos.x;
			self.currentY = tempPos.y;
		
			self.arrowLineStartPoint.x = self.currentX;
			self.arrowLineStartPoint.y = self.currentY;

			self.shapeData = [];
			self.shapeData.push([self.currentX, self.currentY]);
			switch (self.shapeType){
				case shapeTypeEnum.failure:
					self.shape = new Failure({
						shape:{
							x:self.currentX,
							y:self.currentY,
							width:20,
							height:20
						},
						style: {
							stroke: self.color,
							lineWidth:self.lineWidth
						},
					})
					break;
				
				case shapeTypeEnum.success:
					self.shape = new Successright({
						shape:{
							x:self.currentX - 10,
							y:self.currentY - 10,
							width:20,
							height:20
						},
						style: {
							stroke: self.color,
							fill: 'none',
							lineWidth:self.lineWidth
						},
					})
					break;
				
				
				
				case shapeTypeEnum.arrow:
					self.shape = new Arrow({
						shape:{
							x:self.currentX - 15,
							y:self.currentY - 10,
							width:30,
							height:20
						},
						style: {
							stroke: self.color,
							fill: self.fillColor,
							lineWidth:self.lineWidth
						},
					})
					break;
					
				case shapeTypeEnum.hlight_texttag:
					if(self.textTagShape){
						self.textTagShape.attr({
							shape: {
								x:self.currentX - 40,
								y:self.currentY - 20
							},
							style:{
								text: self.roomHandle.selfUser.name
							}
						});
						self.roomHandle.sendAddAnnotation(self.boardId, self.textTagShape, self.shapeType, self.color, self.lineWidth, self.arrowtype, self.fillType);
					}else{
						self.textTagShape = new Texttag({
							shape:{
								x:self.currentX - 40,
								y:self.currentY - 20,
								width:80,
								height:40
							},
							style: {
								stroke: "red",
								fill: "red",
								text: self.roomHandle.selfUser.name,
								truncate:{
									outerWidth:50
								},
								lineWidth:self.lineWidth
							},
						})
						self.textTagShape.id = Math.uuid();
                        self.shapeGroup.add(self.textTagShape);
						self.roomHandle.sendAddAnnotation(self.boardId, self.textTagShape, self.shapeType, self.color, self.lineWidth, self.arrowtype, self.fillType);
					}
					if(self.shape){
						self.shape = null; // 如果其他形状存在,清空,防止再重复绘制其他画笔
					}
					break;
				
				case shapeTypeEnum.rhomb:
					self.shape = new zrender.Polygon({
						shape:{
							points: [],
							smooth:0,
						},
						style: {
							stroke: self.color,
							fill: 'none',
							lineWidth:self.lineWidth
						},
					})
					break;
					
				case shapeTypeEnum.line:
					self.shape = new zrender.Arrowline({
						shape:{
							arrowlength: 10,
							arrowtype: self.arrowtype
						},
						style: {
							stroke: self.color,
							lineWidth:self.lineWidth
						},
					})
					break;
				
					
				case shapeTypeEnum.polyline:
					self.shape = new zrender.Polyline({
						shape:{
							points: [],
							smooth:0,
						},
						style: {
							stroke: self.color,
							lineWidth:self.lineWidth
						},
					})
					break;
					
				case shapeTypeEnum.rect:
					self.shape = new zrender.Rect({
						shape:{
							x:self.currentX,
							y:self.currentY
						},
						style: {
							stroke: self.color,
							fill: self.fillColor,
							lineWidth:self.lineWidth
						},
					})
					break;
				
				case shapeTypeEnum.ellipse:
					
					self.shape = new zrender.Ellipse({
						
						shape:{
							cx:self.currentX,
							cy:self.currentY,
							rx:0,
							ry:0,
						},
						style: {
							stroke: self.color,
							fill: self.fillColor,
							lineWidth:self.lineWidth
						},
					})
					break;
				
				default:
					break;
			}
			if(self.shape){
				self.shape.id = Math.uuid();
				if(self.shapeType == shapeTypeEnum.success || self.shapeType == shapeTypeEnum.failure || self.shapeType == shapeTypeEnum.arrow){
					var shapeObj = {
						shape:self.shape,
						shapeType:self.shapeType,
						color:self.color,
						lineWidth:self.lineWidth,
						arrowtype:self.arrowtype,
						fillType:self.fillType,
					}
					self.shapeArray.push(shapeObj);
					self.undoArray.push({
						type: 'add',
						shapeObj: shapeObj
					})
					self.roomHandle.sendAddAnnotation(self.boardId, self.shape, self.shapeType, self.color, self.lineWidth, self.arrowtype, self.fillType);
				}
                self.shapeGroup.add(self.shape);
			}else{
				return;
			}
			
		}
		self.renderDiv.ontouchmove = function(e){
			if(self.isAllowEditImg){
				return;
			}
			
			//使用更精确的坐标计算
			var rect = this.getBoundingClientRect();
			var touch = e.touches[0];
			var rawX = touch.clientX - rect.left;
			var rawY = touch.clientY - rect.top;
			
			var tempPos = self.handleScaledXY(rawX, rawY);
			self.x = tempPos.x;
			self.y = tempPos.y;
				
			if(self.shapeType == shapeTypeEnum.eraser){
				self.isEraser = true;
				var realX = self.x - self.eraserImageWidth / 2;
				var realY = self.y - self.eraserImageHeight / 2;
				if(self.eraserShape){
					self.eraserShape.attr('position', [realX, realY]);
				}
				
				self.shapeArray.forEach(function(tempShape){
					switch(tempShape.shapeType){
						case shapeTypeEnum.line:
							var shape = tempShape.shape.shape;
							var x1 = shape.x1;
							var x2 = shape.x2;
							var y1 = shape.y1;
							var y2 = shape.y2;
							if(x1 < x2 && y1 < y2){
								if(self.x >= x1 && self.x <= x2 && self.y >= y1 && self.y <= y2){
								self.excuteRemoveAnnotation(tempShape);
								}
							}else if(x1 > x2 && y1 < y2){
								if(self.x >= x2 && self.x <= x1 && self.y >= y1 && self.y <= y2){
								self.excuteRemoveAnnotation(tempShape);
								}
							}else if(x1 < x2 && y1 > y2){
								if(self.x >= x1 && self.x <= x2 && self.y >= y2 && self.y <= y1){
								self.excuteRemoveAnnotation(tempShape);
								}
							}else if(x1 > x2 && y1 > y2){
								if(self.x >= x2 && self.x <= x1 && self.y >= y2 && self.y <= y1){
								self.excuteRemoveAnnotation(tempShape);
								}
							}
							break;
						case shapeTypeEnum.polyline:
							// console.log('+++SDK shapeType polyline')
							var points = tempShape.shape.shape.points;
							var minLocationWidth = self.x - self.eraserImageWidth / 2;
							var maxLocationWidth = self.x + self.eraserImageWidth / 2;
							var minLocationHeight = self.y - self.eraserImageWidth / 2;
							var maxLocationHeight = self.y + self.eraserImageWidth / 2;
							if(points.length > 0){
								points.forEach(function (point){
								var pointX = point[0];
								var pointY = point[1];
								if(pointX >= minLocationWidth && pointX <= maxLocationWidth && pointY >= minLocationHeight && pointY <= maxLocationHeight){
									self.excuteRemoveAnnotation(tempShape);
								}
								})
							}
							break;
						case shapeTypeEnum.success:
							var width = tempShape.shape.shape.width;
							var height = tempShape.shape.shape.height;
							var successX = tempShape.shape.shape.x;
							var successY = tempShape.shape.shape.y;
							if(self.x >= successX && self.x <= successX + width && self.y >= successY && self.y <= successY + height){
								self.excuteRemoveAnnotation(tempShape);
							}
							break;
						case shapeTypeEnum.failure:
							var width = tempShape.shape.shape.width;
							var height = tempShape.shape.shape.height;
							var failureX = tempShape.shape.shape.x;
							var failureY = tempShape.shape.shape.y;
							if(self.x >= failureX && self.x <= failureX + width && self.y >= failureY && self.y <= failureY + height){
								self.excuteRemoveAnnotation(tempShape);
							}
							break;
						case shapeTypeEnum.rhomb:
							var pointOne = tempShape.shape.shape.points[0];
							var pointTwo = tempShape.shape.shape.points[1];
							var pointThree = tempShape.shape.shape.points[2];
							var pointFour = tempShape.shape.shape.points[3];
			
							var x1 = pointOne[0];
							var x2 = pointThree[0];
							var y1 = pointTwo[1];
							var y2 = pointFour[1];
			
							if(self.x >= x1 && self.x <= x2 && self.y >= y1 && self.y <= y2){
								// console.log('+++SDK 清除菱形1');
								self.excuteRemoveAnnotation(tempShape);
								break;
							}else if(self.x <= x1 && self.x >= x2 && self.y >= y1 && self.y <= y2){
								// console.log('+++SDK 清除菱形2');
								self.excuteRemoveAnnotation(tempShape);
								break;
							}else if(self.x >= x1 && self.x <= x2 && self.y <= y1 && self.y >= y2){
								// console.log('+++SDK 清除菱形3');
								self.excuteRemoveAnnotation(tempShape);
								break;
							}else if(self.x <= x1 && self.x >= x2 && self.y <= y1 && self.y >= y2){
								// console.log('+++SDK 清除菱形4');
								self.excuteRemoveAnnotation(tempShape);
								break;
							}
			
							break;
						case shapeTypeEnum.arrow:
							var width = tempShape.shape.shape.width;
							var height = tempShape.shape.shape.height;
							var arrowX = tempShape.shape.shape.x;
							var arrowY = tempShape.shape.shape.y;
							if(self.x >= arrowX && self.x <= arrowX + width && self.y >= arrowY && self.y <= arrowY + height){
								self.excuteRemoveAnnotation(tempShape);
							}
							break;
						case shapeTypeEnum.ellipse:
							var cx = tempShape.shape.shape.cx;
							var cy = tempShape.shape.shape.cy;
							var rx = tempShape.shape.shape.rx;
							var ry = tempShape.shape.shape.ry;
							// 如果点击点在椭圆范围内
							if(self.x >= cx - rx && self.x <= cx + rx && self.y >= cy - ry && self.y <= cy + ry){
								self.excuteRemoveAnnotation(tempShape);
							}
							break;
						case shapeTypeEnum.rect:
							var rectWidth = tempShape.shape.shape.width;
							var rectHeight = tempShape.shape.shape.height;
							var rectX = tempShape.shape.shape.x;
							var rectY = tempShape.shape.shape.y;
							// 如果点击点在矩形范围内
							if(rectWidth >= 0 && rectHeight >=0){
								if(self.x >= rectX && self.x <= rectX + rectWidth && self.y >= rectY && self.y <= rectY + rectHeight){
								self.excuteRemoveAnnotation(tempShape);
								}
							}else if(rectWidth <= 0 && rectHeight >= 0){
								if(self.x >= rectX + rectWidth && self.x <= rectX && self.y >= rectY && self.y <= rectY + rectHeight){
								self.excuteRemoveAnnotation(tempShape);
								}
							}else if(rectWidth >= 0 && rectHeight <= 0){
								if(self.x >= rectX && self.x <= rectX + rectWidth && self.y >= rectY + rectHeight && self.y <= rectY){
								self.excuteRemoveAnnotation(tempShape);
								}
							}else if(rectWidth <= 0 && rectHeight <= 0){
								if(self.x >= rectX + rectWidth && self.x <= rectX && self.y >= rectY + rectHeight && self.y <= rectY){
								self.excuteRemoveAnnotation(tempShape);
								}
							}
			
							break;
						
						default: 
						break;
					}
				})
				return;
			}

			self.shapeData.push([self.x, self.y]);
			var rhombData = [[self.currentX, self.currentY + (self.y-self.currentY)/2],[self.currentX + (self.x - self.currentX)/2, self.y],[self.x, self.currentY + (self.y-self.currentY)/2],[self.currentX + (self.x - self.currentX)/2, self.currentY]]
			switch (self.shapeType){
				
				case shapeTypeEnum.rhomb:
					self.shape.attr('shape',{
						points: rhombData
					})
					break;
					
				case shapeTypeEnum.line:
					var shapePoint = {
						x1: self.arrowLineStartPoint.x,
						y1: self.arrowLineStartPoint.y,
						x2:self.x,
						y2:self.y
					}
					self.shape.attr('shape',{
						x1: self.arrowLineStartPoint.x,
						y1: self.arrowLineStartPoint.y,
						x2:self.x,
						y2:self.y
					})
					break;
					
				case shapeTypeEnum.polyline:
					self.shape.attr('shape',{
						points: self.shapeData
					})
					break;
					
				case shapeTypeEnum.rect:
					self.shape.attr('shape',{
						width:self.x-self.currentX,
						height:self.y-self.currentY
					})
					break;
					
				case shapeTypeEnum.ellipse:
					self.shape.attr('shape',{
						cx:self.x + (self.currentX-self.x)/2,
						cy:self.y + (self.currentY-self.y)/2,
						rx:Math.abs(self.x-self.currentX)/2,
						ry:Math.abs(self.y-self.currentY)/2,
					})
					break;
				case shapeTypeEnum.hlight_point:
					var width = self.hlightPointInfo.width;
					var height = self.hlightPointInfo.height;
					var realX = self.x - width / 2;
					var realY = self.y - height / 2;
					self.hlightPointShape.attr('position',[realX ,realY]);
					if(self.hlightPointInfo.isSend){
						self.hlightPointInfo.isSend = false;
						
						self.roomHandle.sendAddAnnotation(self.boardId, self.hlightPointShape, shapeTypeEnum.hlight_point, self.color, self.lineWidth, self.arrowtype, self.fillType);
						setTimeout(function(){
							self.hlightPointInfo.isSend = true;
						}, self.hlightPointInfo.intervalTime)
					}
					break;
					
				default:
					break;
			}
		}
		
		self.renderDiv.ontouchend = function(){
			if(self.isAllowEditImg){
				return;
			}
			
			if(self.shapeType == shapeTypeEnum.txt || self.shapeType == shapeTypeEnum.hlight_texttag || self.shapeType == shapeTypeEnum.mouse || self.shapeType == shapeTypeEnum.success || self.shapeType == shapeTypeEnum.failure || self.shapeType == shapeTypeEnum.arrow){
				return;
			}

			if(self.shapeType == shapeTypeEnum.eraser){
				self.isEraser = false;
				return;
			}
			
			var shapeObj = {
				shape:self.shape,
				shapeType:self.shapeType,
				color:self.color,
				lineWidth:self.lineWidth,
				arrowtype:self.arrowtype,
				fillType:self.fillType,
			}
			self.shapeArray.push(shapeObj);
			self.redoArray = [];
			if(self.shapeType != shapeTypeEnum.hlight_point){
				self.undoArray.push({
					type: 'add',
					shapeObj: shapeObj
				})
				log.debug("++TTTTTTTTTT111111>>>>>>>>>>>");
				self.roomHandle.sendAddAnnotation(self.boardId, self.shape, self.shapeType, self.color, self.lineWidth, self.arrowtype, self.fillType);
			}
		}

        if(self.initType == AnnotationInitTypeEnum.proportional && self.board.backgroundColor != ""){
            self.updateBackgroundColor(self.board.backgroundColor);
        }

        if(self.initType == AnnotationInitTypeEnum.proportional && self.board.backgroundImage != ""){
            self.updateBackgroundImage(self.board.backgroundImage);
        }

        deferred.resolve(); 
        return deferred.promise;	
	}

    // 橡皮擦清除画笔
    Annotation.prototype.excuteRemoveAnnotation = function(removeShape){
        var self = this;
        self.zr.remove(removeShape.shape)
        self.shapeGroup.remove(removeShape.shape);
        self.roomHandle.sendRemoveAnnotation(self.boardId, self.shapeType, removeShape.shape.id)
        self.shapeArray.forEach(function(shapeObj, index){
        if(shapeObj.shape.id == removeShape.shape.id){
                self.undoArray.push({
                    type: 'delete',
                    shapeObj: shapeObj
                })
                self.shapeArray.splice(index, 1);
        }
        })

        /**
         * desc: 解决重连后重复批注删不掉
        * by: 王博 
        * date: 2021/7/15
        */
        var tempList = self.zr.storage._displayList;
        for(var i in tempList){
            if(tempList[i].id == removeShape.shape.id){
                self.zr.remove(tempList[i]);
                self.shapeGroup.remove(tempList[i])
            }
        }
    }
	 
	Annotation.prototype.initEraserShape = function(eraserImgUrl){
		var self = this;
		self.eraserShape = new zrender.Image({
			position: [0, 0],
			scale: [1, 1],
			style: {
				image: eraserImgUrl,
				width: self.eraserImageWidth,
				height: self.eraserImageHeight
			},
			draggable: true
		})
        self.shapeGroup.add(self.eraserShape);
		self.eraserShape.id = Math.uuid();
	}

	Annotation.prototype.removeToBoard = function(removeInfo) {
		log.debug('===annotation removeToBoard');
		var self = this;
		var userKey = 'userId' + removeInfo.operUserId;

        var shapeArray = self.shapeArray;
        var displayList = self.zr.storage.getDisplayList();
        var groupChildren = self.shapeGroup.children();

		if(removeInfo.annotationType == shapeTypeEnum.clear){
			if(removeInfo.operUserId == self.roomHandle.selfUser.id){
				for(var k in self.shapeArray){
					self.zr.remove(self.shapeArray[k].shape);
                    self.shapeGroup.remove(self.shapeArray[k].shape)
				}
				self.shapeArray = [];
			}else{
				for(var k in self.otherShapeMap){
					if(k == userKey){
						for(var j in self.otherShapeMap[k]){
							self.zr.remove(self.otherShapeMap[k][j].shape);
                            self.shapeGroup.remove(self.otherShapeMap[k][j].shape);

                            for(var i = displayList.length - 1; i >= 0; i--){
                                if(displayList[i].id == self.otherShapeMap[k][j].shape.id){
                                    self.zr.remove(displayList[i])
                                }
                            }   
                
                            for(var m = groupChildren.length - 1; m >= 0; m--){
                                if(groupChildren[m].id == self.otherShapeMap[k][j].shape.id){
                                    self.shapeGroup.remove(groupChildren[m])
                                }
                            }
						}
					}
				}
			}
			
			
			delete self.otherShapeMap[userKey];
			
		}else if(removeInfo.annotationType == shapeTypeEnum.eraser){
			if(self.hlightPointShape && self.hlightPointShape.id == removeInfo.annotationId){
				self.clearCurrHlightPoint();
			}else if(self.textTagShape && self.textTagShape.id == removeInfo.annotationId){
				self.clearCurrHlightPoint();
			}else if(removeInfo.operUserId == self.roomHandle.selfUser.id){
				for(var k in self.shapeArray){
					if(self.shapeArray[k].shape.id == removeInfo.annotationId){
						self.zr.remove(self.shapeArray[k].shape);
                        self.shapeGroup.remove(self.shapeArray[k].shape);
						self.shapeArray.splice(k,1);
					}
				}
			}else{
				for(var k in self.otherShapeMap){
					if(k == userKey){
						for(var j in self.otherShapeMap[k]){
							if(self.otherShapeMap[k][j].id == removeInfo.annotationId){
								self.zr.remove(self.otherShapeMap[k][j].shape);
                                self.shapeGroup.remove(self.otherShapeMap[k][j].shape);
							}
						}
					}
				}

				/**
				 * desc: 解决重连后重复批注删不掉
				 * by: 王博 
				 * date: 2021/7/15
				 */
				var tempList = self.zr.storage._displayList;
				for(var i in tempList){
					if(tempList[i].id == removeInfo.annotationId){
						self.zr.remove(tempList[i]);
                        self.shapeGroup.remove(tempList[i]);
					}
				}
			}
		}else if(removeInfo.annotationType == shapeTypeEnum.img){

            for(var k in self.otherShapeMap){
                if(k == userKey){
                    for(var j = self.otherShapeMap[k].length - 1; j >= 0; j--){
                        // 从各个维护的列表中删除当前批注对象
                        if(self.otherShapeMap[k][j].shape.id == removeInfo.annotationId || (removeInfo.Img && self.otherShapeMap[k][j].shape.id == removeInfo.Img.im.id)){
                            self.zr.remove(self.otherShapeMap[k][j].shape);
                            self.shapeGroup.remove(self.otherShapeMap[k][j].shape);
                            self.otherShapeMap[k].splice(j, 1);
                        }
                    }
                }
            }
		}
	}
	
	
	function replaceRgba(obj){
		return "rgba(" + obj.r + "," +  obj.g + "," + obj.b + "," + parseFloat(obj.a/255) + ")";
	}

    function replaceRgb(obj){
		return "rgb(" + obj.r + "," +  obj.g + "," + obj.b + ")";
	}

    Annotation.prototype.drawTextToBoard = function(annotationInfo) {
        var self = this;
        var txt = annotationInfo.txt;
        var txi = txt.txi
        var txs = txi.txs
        var objectBase = txt.objectBase.objectBase;
        var shapeType = parseInt(txt.objectBase.type);
		var lineWidth = parseInt(objectBase.lineWidth);
		var arrowtype = parseInt(objectBase.arrow);
		var color = replaceRgba(objectBase.lineColor);
		var fillColor = replaceRgba(objectBase.fillColor);

        var text = new zrender.Text({
            style: {
                text: txs[0].content,
                fontSize: txi.fontSize,
                fontStyle: txi.fontStyle,
                fontWeight: txi.fontWeight,
                fill: replaceRgb(txi.fontColor)
            },
            
            position: [txt.objectBase.start.x * self.roomHandle.boardInputXRatio, txt.objectBase.start.y * self.roomHandle.boardInputYRatio],
            origin: [txt.objectBase.start.x * self.roomHandle.boardInputXRatio, txt.objectBase.start.y * self.roomHandle.boardInputYRatio],
            onmouseover: mouseOverForEraser
        })

        self.shapeGroup.add(text);
        
        var shapeObj = {
            id: annotationInfo.annotationId,
            shape: text,
            shapeType: shapeType,
            lineWidth: lineWidth,
            arrowtype: arrowtype,
            color: color,
            fillColor: fillColor
        }

        if(annotationInfo.operUserId === self.roomHandle.selfUser.id){
            self.shapeArray.push(shapeObj);
        }else{
            var userKey = 'userId' +  objectBase.ownerUserId
            if(self.otherShapeMap[userKey]){
                self.otherShapeMap[userKey].push(shapeObj);
            }else{
                self.otherShapeMap[userKey] = [];
                self.otherShapeMap[userKey].push(shapeObj);
            }
        }

        function mouseOverForEraser(params) {
			if(self.shapeType == shapeTypeEnum.eraser && self.isEraser){
				for(var k in self.shapeArray){
					if(self.shapeArray[k].shape.id == params.target.parent.id){
						self.zr.remove(params.target.parent)
                        self.shapeGroup.remove(params.target.parent)
						self.roomHandle.sendRemoveAnnotation(self.boardId,self.shapeType,self.shapeArray[k].shape.id)
						self.undoArray.push({
							type: 'delete',
							shapeObj: self.shapeArray[k]
						})
						self.shapeArray.splice(k,1);
					}
				}
			}
		}
    }
	
	//远端获取的注释,画在画板上
	Annotation.prototype.drawToBoard = function(annotationInfo) {
		var self = this;
		var shapeType = parseInt(annotationInfo.objectBase.type);
		var lineWidth = parseInt(annotationInfo.objectBase.lineWidth);
		var arrowtype = parseInt(annotationInfo.objectBase.arrow);
		var color = replaceRgba(annotationInfo.objectBase.lineColor);
		var fillColor = replaceRgba(annotationInfo.objectBase.fillColor);
		var shape = null;

        function mouseOverForEraser(params) {
            if(self.shapeType == shapeTypeEnum.eraser && self.isEraser){
                for(var k in self.shapeArray){
                    if(params.target.id && self.shapeArray[k].shape.id == params.target.id){
                        self.zr.remove(self.shapeArray[k].shape)
                        self.shapeGroup.remove(self.shapeArray[k].shape)
                        /**
                         * desc: 解决重连后重复批注删不掉
                            * by: 王博 
                            * date: 2021/7/15
                            */
                        var tempList = self.zr.storage._displayList;
                        for(var i in tempList){
                            if(tempList[i].id == params.target.id){
                                self.zr.remove(tempList[i]);
                                self.shapeGroup.remove(tempList[i]);
                            }
                        }
    
                        self.roomHandle.sendRemoveAnnotation(self.boardId,self.shapeType,self.shapeArray[k].shape.id)
                        self.shapeArray.splice(k,1);
                    }
                }
            }
        }

		switch (shapeType){
			
			case shapeTypeEnum.rhomb:
				var currentX = annotationInfo.start.x;
				var currentY = annotationInfo.start.y;
				var x = annotationInfo.end.x;
				var y = annotationInfo.end.y;
				
				log.debug("===annotation.drawToBoard(),room.boardInputXRatio:"+ self.roomHandle.boardInputXRatio+",room.boardInputYRatio:"+self.roomHandle.boardInputYRatio);
				log.debug("===annotation.drawToBoard(),shapeTypeEnum.rhomb,currentX:",currentX+",currentY:",currentY+",x:"+x+",y:"+y);
				
				var inputCurrentX = Math.round(currentX * self.roomHandle.boardInputXRatio);
		        var inputCurrentY = Math.round(currentY * self.roomHandle.boardInputYRatio);
		        var inputX = Math.round(x * self.roomHandle.boardInputXRatio);
		        var inputY = Math.round(y * self.roomHandle.boardInputYRatio);
		        log.debug("===annotation.drawToBoard(),ishapeTypeEnum.rhomb,nputCurrentX:",inputCurrentX+",inputCurrentY:",inputCurrentY+",inputX:"+inputX+",inputY:"+inputY);
				
				var rhombData = [[inputCurrentX, inputCurrentY + (inputY-inputCurrentY)/2],[inputCurrentX + (inputX - inputCurrentX)/2, inputY],[inputX, inputCurrentY + (inputY-inputCurrentY)/2],[inputCurrentX + (inputX - inputCurrentX)/2, inputCurrentY]]

				shape = new zrender.Polygon({
					shape:{
						points: rhombData,
						smooth:0,
					},
					style: {
						stroke: color,
						fill: 'none',
						lineWidth:lineWidth
					},
                    zlevel: self.normalZlevel,
					onmouseover:mouseOverForEraser
				})
				break;
				
			case shapeTypeEnum.line:
				var x1 = annotationInfo.start.x;
				var y1 = annotationInfo.start.y;
				var x2 = annotationInfo.end.x;
				var y2 = annotationInfo.end.y;
				
				log.debug("===annotation.drawToBoard(),shapeTypeEnum.line,x1:",x1+",y1:",y1+",x2:"+x2+",y2:"+y2);
				
				var inputx1 = Math.round(x1 * self.roomHandle.boardInputXRatio);
		        var inputy1 = Math.round(y1 * self.roomHandle.boardInputYRatio);
		        var inputx2 = Math.round(x2 * self.roomHandle.boardInputXRatio);
		        var inputy2 = Math.round(y2 * self.roomHandle.boardInputYRatio);
		        
		        log.debug("===annotation.drawToBoard(),shapeTypeEnum.line,inputx1:",inputx1+",inputy1:",inputy1+",inputx2:"+inputx2+",inputy2:"+inputy2);
				
				shape = new zrender.Arrowline({
					shape:{
						x1: inputx1,
						y1: inputy1,
						x2: inputx2,
						y2: inputy2,
						arrowlength: 10,
						arrowtype: arrowtype
					},
					style: {
						stroke: color,
						lineWidth:lineWidth
					},
                    zlevel: self.normalZlevel,
					onmouseover:mouseOverForEraser
				})
				break;
			
			
			case shapeTypeEnum.failure:
				var x = annotationInfo.start.x;
				var y = annotationInfo.start.y;
				var width = annotationInfo.end.x - annotationInfo.start.x;
				var height = annotationInfo.end.y - annotationInfo.start.y;
				
				log.debug("===annotation.drawToBoard(),shapeTypeEnum.failure,x:",x+",y:",y+",width:"+width+",height:"+height);
				
				var inputX = Math.round(x * self.roomHandle.boardInputXRatio);
		        var inputY = Math.round(y * self.roomHandle.boardInputYRatio);
		        var inputWidth = Math.round(width * self.roomHandle.boardInputXRatio);
		        var inputHeight = Math.round(height * self.roomHandle.boardInputYRatio);
		        
		        log.debug("===annotation.drawToBoard(),shapeTypeEnum.failure,inputX:",inputX+",inputY:",inputY+",inputWidth:"+inputWidth+",inputHeight:"+inputHeight);
				
				shape = new Failure({
					shape:{
						x:inputX,
						y:inputY,
						width:inputWidth,
						height:inputHeight
					},
					style: {
						stroke: color,
						lineWidth:lineWidth
					},
                    zlevel: self.normalZlevel,
					onmouseover:mouseOverForEraser
				})
				
				break;
				
			case shapeTypeEnum.success:
			
				var x = annotationInfo.start.x;
				var y = annotationInfo.start.y;
				var width = annotationInfo.end.x - annotationInfo.start.x;
				var height = annotationInfo.end.y - annotationInfo.start.y;
				
				log.debug("===annotation.drawToBoard(),shapeTypeEnum.success,x:",x+",y:",y+",width:"+width+",height:"+height);
				
				var inputX = Math.round(x * self.roomHandle.boardInputXRatio);
		        var inputY = Math.round(y * self.roomHandle.boardInputYRatio);
		        var inputWidth = Math.round(width * self.roomHandle.boardInputXRatio);
		        var inputHeight = Math.round(height * self.roomHandle.boardInputYRatio);
		        
		        log.debug("===annotation.drawToBoard(),shapeTypeEnum.success,inputX:",inputX+",inputY:",inputY+",inputWidth:"+inputWidth+",inputHeight:"+inputHeight);
				
				shape = new Successright({
					shape:{
						x:inputX,
						y:inputY,
						width:inputWidth,
						height:inputHeight
					},
					style: {
						stroke: color,
						fill: 'none',
						lineWidth:lineWidth
					},
                    zlevel: self.normalZlevel,
					onmouseover:mouseOverForEraser
				})
				
				break;
				
			case shapeTypeEnum.arrow:
			
				var x = annotationInfo.start.x;
				var y = annotationInfo.start.y;
				var width = annotationInfo.end.x - annotationInfo.start.x;
				var height = annotationInfo.end.y - annotationInfo.start.y;
				
				log.debug("===annotation.drawToBoard(),shapeTypeEnum.arrow,x:",x+",y:",y+",width:"+width+",height:"+height);
				
				var inputX = Math.round(x * self.roomHandle.boardInputXRatio);
		        var inputY = Math.round(y * self.roomHandle.boardInputYRatio);
		        var inputWidth = Math.round(width * self.roomHandle.boardInputXRatio);
		        var inputHeight = Math.round(height * self.roomHandle.boardInputYRatio);
		        
		        log.debug("===annotation.drawToBoard(),shapeTypeEnum.arrow,inputX:",inputX+",inputY:",inputY+",inputWidth:"+inputWidth+",inputHeight:"+inputHeight);
				
				shape = new Arrow({
					shape:{
						x:inputX,
						y:inputY,
						width:inputWidth,
						height:inputHeight
					},
					style: {
						stroke: color,
						fill: fillColor,
						lineWidth:lineWidth
					},
                    zlevel: self.normalZlevel,
					onmouseover:mouseOverForEraser
				})
				
				break;
			case shapeTypeEnum.hlight_texttag:
				var user = self.roomHandle.getUser(annotationInfo.objectBase.ownerUserId);
				var x = annotationInfo.start.x;
				var y = annotationInfo.start.y;
				var width = annotationInfo.end.x - annotationInfo.start.x;
				var height = annotationInfo.end.y - annotationInfo.start.y;
				
				log.debug("===annotation.drawToBoard(),shapeTypeEnum.hlight_texttag,x:",x+",y:",y+",width:"+width+",height:"+height);
				
				var inputX = Math.round(x * self.roomHandle.boardInputXRatio);
		        var inputY = Math.round(y * self.roomHandle.boardInputYRatio);
		        var inputWidth = Math.round(width * self.roomHandle.boardInputXRatio);
		        var inputHeight = Math.round(height * self.roomHandle.boardInputYRatio);
		        
		        log.debug("===annotation.drawToBoard(),shapeTypeEnum.hlight_texttag,inputX:",inputX+",inputY:",inputY+",inputWidth:"+inputWidth+",inputHeight:"+inputHeight);
				
				if(!user){
					return;
				}
				if(self.textTagShape){
					self.textTagShape.attr({
						shape: {
							x:inputX,
							y:inputY
						},
						style:{
							text: user.name
						},
                        zlevel: self.normalZlevel,
					});
					
				}else{
					self.textTagShape = new Texttag({
						shape:{
							x:inputX,
							y:inputY,
							width:inputWidth,
							height:inputHeight
						},
						style: {
							stroke: "red",
							fill: "red",
							text: user.name,
							truncate:{
								outerWidth:50
							},
							lineWidth:lineWidth
						},
                        zlevel: self.normalZlevel,
						onmouseover:mouseOverForEraser
					})
                    self.shapeGroup.add(self.textTagShape);
					self.textTagShape.id = annotationInfo.objectBase.annotationId;
					
				}
				break;
			case shapeTypeEnum.polyline:
				var points = [];
				console.log("===annotation.drawToBoard(),shapeTypeEnum.polyline,points:",points);
				for(var k in annotationInfo.points){
					var x = annotationInfo.points[k].x;
					var y = annotationInfo.points[k].y;
					
					//console.log("===annotation.drawToBoard(),shapeTypeEnum.polyline,x:",x+",y:",y);
				
				    var inputX = Math.round(x * self.roomHandle.boardInputXRatio);
		            var inputY = Math.round(y * self.roomHandle.boardInputYRatio);
		        
		            //console.log("===annotation.drawToBoard(),shapeTypeEnum.polyline,inputX:",inputX+",inputY:",inputY);
					
					var point = [inputX,inputY]
					points.push(point);
				}
				shape = new zrender.Polyline({
					shape:{
						points: points,
						smooth:0,
					},
					style: {
						stroke: color,
						lineWidth:lineWidth
					},
                    zlevel: self.normalZlevel,
					onmouseover:mouseOverForEraser
				})
				break;
				
			case shapeTypeEnum.rect:
				var x = annotationInfo.start.x;
				var y = annotationInfo.start.y;
				var width = annotationInfo.end.x - annotationInfo.start.x;
				var height = annotationInfo.end.y - annotationInfo.start.y;
				
				log.debug("===annotation.drawToBoard(),shapeTypeEnum.rect,x:",x+",y:",y+",width:"+width+",height:"+height);
				
				var inputX = Math.round(x * self.roomHandle.boardInputXRatio);
		        var inputY = Math.round(y * self.roomHandle.boardInputYRatio);
		        var inputWidth = Math.round(width * self.roomHandle.boardInputXRatio);
		        var inputHeight = Math.round(height * self.roomHandle.boardInputYRatio);
		        
		        log.debug("===annotation.drawToBoard(),shapeTypeEnum.rect,inputX:",inputX+",inputY:",inputY+",inputWidth:"+inputWidth+",inputHeight:"+inputHeight);
				
				shape = new zrender.Rect({
					shape:{
						x:inputX,
						y:inputY,
						width:inputWidth,
						height:inputHeight
					},
					style: {
						stroke: color,
						fill: fillColor,
						lineWidth:lineWidth
					},
                    zlevel: self.normalZlevel,
					onmouseover:mouseOverForEraser
				})
				break;
			
			case shapeTypeEnum.ellipse:
			    var startX = annotationInfo.start.x;
			    var endX =  annotationInfo.end.x;
			    var startY = annotationInfo.start.y;
			    var endY = annotationInfo.end.y;
			    log.debug("===annotation.drawToBoard(),shapeTypeEnum.ellipse,startX :",startX+",endX:",endX+",startY:"+startY+",endY:"+endY); 
			    
			    var inputStartX = Math.round(startX * self.roomHandle.boardInputXRatio);
		        var inputEndX = Math.round(endX * self.roomHandle.boardInputXRatio);
		        var inputStartY = Math.round(startY * self.roomHandle.boardInputYRatio);
		        var inputEndY = Math.round(endY * self.roomHandle.boardInputYRatio);
			     
			    log.debug("===annotation.drawToBoard(),shapeTypeEnum.ellipse,inputStartX :",inputStartX+",inputEndX:",inputEndX+",inputStartY:"+inputStartY+",inputEndY:"+inputEndY); 
			      
				var rx = (inputEndX - inputStartX)/2;
				var ry = (inputEndY - inputStartY)/2;
				var cx = inputStartX + rx;
				var cy = inputStartY + ry;
				
				shape = new zrender.Ellipse({
					shape:{
						cx:cx,
						cy:cy,
						rx:rx,
						ry:ry,
					},
					style: {
						stroke: color,
						fill: fillColor,
						lineWidth:lineWidth
					},
                    zlevel: self.normalZlevel,
					onmouseover:mouseOverForEraser
				})
				break;
			case shapeTypeEnum.hlight_point:
				var x = annotationInfo.start.x;
				var y = annotationInfo.start.y;
				var width = self.hlightPointInfo.width;
				var height = self.hlightPointInfo.height;
				
				console.log("===annotation.drawToBoard(),shapeTypeEnum.hlight_point,x:",x+",y:",y+",width:"+width+",height:"+height);
				
				var inputX = Math.round(x * self.roomHandle.boardInputXRatio);
		        var inputY = Math.round(y * self.roomHandle.boardInputYRatio);
		        var inputWidth = Math.round(width * self.roomHandle.boardInputXRatio);
		        var inputHeight = Math.round(height * self.roomHandle.boardInputYRatio);
		        
		         log.debug("===annotation.drawToBoard(),shapeTypeEnum.hlight_point,inputX:",inputX+",inputY:",inputY+",inputWidth:"+inputWidth+",inputHeight:"+inputHeight);
				
				if(!self.hlightPointShape){
					self.hlightPointShape = new zrender.Image({
						position: [inputX, inputY],
			            scale: [1, 1],
			            style: {
			                image: self.hlightPointInfo.imgUrl,
			                width: inputWidth,
			                height:inputHeight
			            },
			            draggable: true,
                        zlevel: self.normalZlevel
					})
                    self.shapeGroup.add(self.hlightPointShape);
					self.hlightPointShape.id = annotationInfo.objectBase.annotationId;
				}else{
					self.hlightPointShape.attr('position',[inputX - inputWidth / 2 ,inputY - inputHeight / 2]);
				}
				
				break;
			case shapeTypeEnum.img:
			    console.log("===annotation.drawToBoard(),shapeTypeEnum.img, annotationInfo:",annotationInfo);
			    

				var rotate = degree2mathPI(annotationInfo.im.rotate);
                var img = null;

                var shapeObj = {
                    id: annotationInfo.im.id,
					shape: null,
					shapeType: shapeType,
                    lineWidth: lineWidth,
                    arrowtype: arrowtype,
                    color: color,
                    fillColor: fillColor
				}

                // 自己的批注图片渲染(在关闭再打开白板时,自己原有图片再次渲染)
                if(annotationInfo.objectBase.ownerUserId === self.roomHandle.selfUser.id){
                    for (var i = 0; i < self.roomHandle.annotation2boardId[annotationInfo.objectBase.objectId].storageArr.length; i++) {
                        var tempShape = self.roomHandle.annotation2boardId[annotationInfo.objectBase.objectId].storageArr[i];
                        if (tempShape.annotationType == shapeTypeEnum.img && tempShape.annotationId == annotationInfo.im.id && tempShape.operUserId == self.roomHandle.selfUser.id) {
                            img = tempShape.imgZrender;
                            shapeObj.shape = img;
                            self.shapeArray.push(shapeObj);
                            break;
                        }
                    }

                }else{
                    // 远端用户的图片渲染
                    img = new zrender.Image({
                        style: {
                            image:annotationInfo.im.file_url,
                            x: annotationInfo.im.x * self.roomHandle.boardInputXRatio,
                            y: annotationInfo.im.y * self.roomHandle.boardInputYRatio,
                            width: annotationInfo.im.w * self.roomHandle.boardInputXRatio,
                            height: annotationInfo.im.h * self.roomHandle.boardInputYRatio
                        },
                        zlevel: annotationInfo.im.layer,
                        id: annotationInfo.im.id,
                        rotation: rotate, 
                        originX: annotationInfo.im.x * self.roomHandle.boardInputXRatio + annotationInfo.im.w / 2 * self.roomHandle.boardInputXRatio,
                        originY: annotationInfo.im.y * self.roomHandle.boardInputYRatio + annotationInfo.im.h / 2 * self.roomHandle.boardInputYRatio
                    });
                    
                    shapeObj.shape = img

                    var userKey = 'userId' + annotationInfo.objectBase.ownerUserId;
                    if(self.otherShapeMap[userKey]){
                        for(var i = self.otherShapeMap[userKey].length - 1; i >= 0; i--){
                            // 先删除otherShapeMap里面的已有的图片对象
                            if(self.otherShapeMap[userKey][i].shape.id === annotationInfo.im.id){
                                self.otherShapeMap[userKey].splice(i, 1);
                            }
                        }
                        self.otherShapeMap[userKey].push(shapeObj);
                    }else{
                        self.otherShapeMap[userKey] = [];
                        self.otherShapeMap[userKey].push(shapeObj);
                    }
                }
				
                self.shapeGroup.add(img);
                self.setAllowEditImg(false);
			    break;
			default:
				break;
		}	
		if(shape){
			var shapeObj = {
				id:annotationInfo.objectBase.annotationId,
				shape:shape
			}
			self.shapeGroup.add(shape);
			shape.id = annotationInfo.objectBase.annotationId;
			if(annotationInfo.objectBase.ownerUserId == self.roomHandle.selfUser.id){
				self.shapeArray.push({
					shape:shape,
					shapeType:shapeType,
					color:color,
					lineWidth:lineWidth,
					arrowtype:arrowtype,
					fillType:fillTypeEnum.none,
				});
			}else{
				var userKey = 'userId' +  annotationInfo.objectBase.ownerUserId
				if(self.otherShapeMap[userKey]){
					self.otherShapeMap[userKey].push(shapeObj);
				}else{
					self.otherShapeMap[userKey] = [];
					self.otherShapeMap[userKey].push(shapeObj);
				}
			}
		}
	}
    //更新远端获取的注释,更新到画板上 by roymond 2022/8/29
    Annotation.prototype.updateDrawToBoard = function(annotationInfo) {
        var self = this;
        var shapeType = parseInt(annotationInfo.objectBase.type);
        
        switch (shapeType){
            case shapeTypeEnum.img:

                self.shapeGroup.eachChild(function(child) {
                    if(child.id === annotationInfo.im.id){
                        self.shapeGroup.remove(child)
                    }
                })

                for(var k in self.shapeArray){
                    if(self.shapeArray[k].shape.id == annotationInfo.im.id){
                        self.zr.remove(self.shapeArray[k].shape);
                        // self.shapeGroup.remove(self.shapeArray[k].shape);
                        self.shapeArray.splice(k,1);
                    }
                }
                
                var rotate = degree2mathPI(annotationInfo.im.rotate);
                var originX = annotationInfo.im.x * self.roomHandle.boardInputXRatio + annotationInfo.im.w * self.roomHandle.boardInputXRatio/2;
                var originY = annotationInfo.im.y * self.roomHandle.boardInputYRatio + annotationInfo.im.h * self.roomHandle.boardInputYRatio /2;

                var img = new zrender.Image({
                    style: {
                        image:annotationInfo.im.file_url,
                        x: annotationInfo.im.x * self.roomHandle.boardInputXRatio,
                        y: annotationInfo.im.y * self.roomHandle.boardInputYRatio,
                        width: annotationInfo.im.w * self.roomHandle.boardInputXRatio,
                        height:annotationInfo.im.h * self.roomHandle.boardInputYRatio
                    },
                    zlevel: annotationInfo.im.layer,
                    id: annotationInfo.im.id,
                    rotation: rotate,
                    origin: [annotationInfo.im.x * self.roomHandle.boardInputXRatio + annotationInfo.im.w * self.roomHandle.boardInputXRatio/2,annotationInfo.im.y * self.roomHandle.boardInputYRatio + annotationInfo.im.h * self.roomHandle.boardInputYRatio /2]
                });

                if(self.roomHandle.style2annotation[annotationInfo.im.id]){
                    img.attr('style', JSON.parse(self.roomHandle.style2annotation[annotationInfo.im.id].style));
                    img.attr('position', JSON.parse(self.roomHandle.style2annotation[annotationInfo.im.id].position));
                    img.attr('scale', JSON.parse(self.roomHandle.style2annotation[annotationInfo.im.id].scale));
                    img.attr('rotation', JSON.parse(self.roomHandle.style2annotation[annotationInfo.im.id].rotation));
                    img.attr('origin', JSON.parse(self.roomHandle.style2annotation[annotationInfo.im.id].origin));
                }

                self.shapeGroup.add(img);
                
                var shapeObj = {
                    shape:img,
                    shapeType:self.shapeType
                }

                if(annotationInfo.objectBase.ownerUserId === self.roomHandle.selfUser.id){
                    self.shapeArray.push(shapeObj);
                }else{
                    var userKey = 'userId' + annotationInfo.objectBase.ownerUserId;
                    if(self.otherShapeMap[userKey]){
                        for(var i = self.otherShapeMap[userKey].length - 1; i >= 0; i--){
                            // 先删除otherShapeMap里面的已有的图片对象
                            if(self.otherShapeMap[userKey][i].shape.id === annotationInfo.im.id){
                                self.otherShapeMap[userKey].splice(i, 1);
                            }
                        }
                        self.otherShapeMap[userKey].push(shapeObj);

                    }else{
                        self.otherShapeMap[userKey] = [];
                        self.otherShapeMap[userKey].push(shapeObj);
                    }
                }

                // 图片属性更新,要把本地缓存的属性也要更新,解决发起人关闭白板后再打开之前的图片位置、缩放、旋转角度丢失问题
                if(self.roomHandle.annotation2boardId[annotationInfo.objectBase.objectId]){
                    for (var i = 0; i < self.roomHandle.annotation2boardId[annotationInfo.objectBase.objectId].storageArr.length; i++) {
                        var tempShape = self.roomHandle.annotation2boardId[annotationInfo.objectBase.objectId].storageArr[i];
                        if (tempShape.Img && tempShape.annotationType == shapeTypeEnum.img && (tempShape.Img.im.id == annotationInfo.im.id || tempShape.annotationId == annotationInfo.im.id) && tempShape.operUserId == annotationInfo.objectBase.ownerUserId) {
                            tempShape.Img.im.x = annotationInfo.im.x;
                            tempShape.Img.im.y= annotationInfo.im.y;
                            tempShape.Img.im.w = annotationInfo.im.w;
                            tempShape.Img.im.h = annotationInfo.im.h;
                            tempShape.Img.im.rotate = annotationInfo.im.rotate;
                            break;
                        }
                    }
                }

                break;
                
            default:
                break;
        }
    }
	
    /**
	 * @desc 注释设置几何形状,如矩形,随机线等   注释类型,如鼠标、注释、橡皮擦
	 * @param {canvasTypeEnum} type - 几何形 和 注释状态  枚举型
	 * 
	 * @example
	 * annotation.setShapeType(shapeTypeEnum.polyline);
	 */
	Annotation.prototype.setShapeType = function(shapeType,mouserUrl) {
		var currentOs = avdEngineHandle.getBrowserDetect().osName;
		if(this.shapeType == shapeTypeEnum.hlight_point && this.hlightPointShape){
			this.clearCurrHlightPoint();
		}
		if(this.eraserShape){
			this.clearCurrEraser()
		}
		// 判断是否是移动端访问,移动端没有鼠标,所以创建一个鼠标shape当成鼠标
		if(shapeType == shapeTypeEnum.eraser && mouserUrl && currentOs == 'Android' || currentOs == 'iOS'){
			this.initEraserShape(mouserUrl)
		}
		if(this.shapeType == shapeTypeEnum.hlight_texttag && this.textTagShape){
			this.clearCurrHlightPoint();
		}
		var imgUrl = ""
		if(mouserUrl){
			imgUrl = "url("+mouserUrl+"),auto"
		}

        if(this.textInfo != null){
            if(shapeType == shapeTypeEnum.txt){
                this.textInfo.setTextEditEnable(true);
                this.zr.handler.proxy.cursor = 'text';
            }else{
                this.textInfo.setTextEditEnable(false);
            }
        }else{
            avdEngineHandle.eraserCustomIconPath = imgUrl;
        }
        this.shapeType = parseInt(shapeType);
	}

	/**
	 * @desc 清除橡皮擦
	 */
    Annotation.prototype.clearCurrEraser = function(){
		var self = this;
		if(self.eraserShape){
			self.zr.remove(self.eraserShape);
            self.shapeGroup.remove(self.eraserShape);
			self.eraserShape = null;
		}
	}

	/**
	 * @desc 注释设置颜色
	 * @param {String} value - 颜色,采用rgb和rgba格式
	 * 
	 * @example
	 * annotation.setColor('rgba(255,0,0,1');
	 */
	Annotation.prototype.setColor = function(color) {
		this.color = color;
		if(this.fillColor != 'none'){
			this.fillColor = color;
		}
	}
	
	/**
	 * @desc 注释设置颜色透明度
	 * @param {String} value - 颜色,采用rgb和rgba格式
	 * 
	 * @example
	 * annotation.setColor('rgba(255,0,0,1');
	 */
	Annotation.prototype.setColorOpacity = function(opacity) {
		
		var arr = this.color.split(',');
		arr[arr.length - 1] = opacity + ')';
		var color = arr.join(',');
		
		console.log(color);
		
		this.color = color;
		if(this.fillColor != 'none'){
			this.fillColor = color;
		}
	}
	
	
	/**
	 * @desc 注释设置线条宽度
	 * @param {int} value - 线条宽度
	 * 
	 * @example
	 * annotation.setLineWidth(1);
	 */
	Annotation.prototype.setLineWidth = function(lineWidth) {
		this.lineWidth = parseInt(lineWidth);
	}
	
	/**
	 * @desc 注释设置箭头宽度
	 * @param {int} value - 箭头宽度
	 * 
	 * @example
	 * annotation.setArrowLength(10);
	 */
	Annotation.prototype.setArrowLength = function(value) {
		this.arrowLength = parseInt(value);
	}
	
	
	/**
	 * @desc 注释设置箭头类别
	 * @param {arrowTypeEnum} value - 箭头类别,枚举型
	 * 
	 * @example
	 * annotation.setArrowType(arrowTypeEnum.double);
	 */
	Annotation.prototype.setArrowType = function(value) {
		this.arrowtype = parseInt(value);
	}
	
	/**
	 * @desc 下载白板,带批注和背景
	 * @example
	 * annotation.download();
	 */
	Annotation.prototype.download = function() {
		if(this.renderBackground == null && this.annoDiv == null){
            return;
        }


		var backgroundCanvas = document.createElement('canvas');
        backgroundCanvas.width = this.renderDiv.width;
        backgroundCanvas.height = this.renderDiv.height;
        var backgroundCtx = backgroundCanvas.getContext("2d");
        if(this.renderBackground){
            backgroundCtx.drawImage(this.renderBackground, 0, 0);
        }
        backgroundCtx.drawImage(this.renderDiv, 0, 0);
		
		downloadimage(backgroundCanvas);
		
		function downloadimage(canvas){
			// 图片导出为 png 格式
			var type = 'png';
			// 返回一个包含JPG图片的<img>元素
			var img_png_src = canvas.toDataURL("image/png");  //将画板保存为图片格式的函数
			// 加工image data,替换mime type
			imgData = img_png_src.replace(_fixType(type),'image/octet-stream');
			// 下载后的问题名
			var filename = '个人画板_' + (new Date()).getTime() + '.' + type;
			// download
			saveFile(imgData,filename);
		}
		
		/**
		* 在本地进行文件保存
		* @param  {String} data     要保存到本地的图片数据
		* @param  {String} filename 文件名
		*/
		function saveFile(data, filename){
		    var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
		    save_link.href = data;
		    save_link.download = filename;
		  
		    var event = document.createEvent('MouseEvents');
		    event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
		    save_link.dispatchEvent(event);
		};
		
		/**
		* 获取mimeType
		* @param  {String} type the old mime-type
		* @return the new mime-type
		*/
		function _fixType(type) {
		    type = type.toLowerCase().replace(/jpg/i, 'jpeg');
		    var r = type.match(/png|jpeg|bmp|gif/)[0];
		    return 'image/' + r;
		};
	}
	/**
	 * @desc 注释设置颜色填充类别
	 * @param {fillTypeEnum} value - 颜色填充类别,枚举型
	 * 
	 * @example
	 * annotation.setFillColor(fillTypeEnum.full);
	 */
	Annotation.prototype.setFillColor = function(value) {
		this.fillType = value;
		if(value == fillTypeEnum.none){
			this.fillColor = 'none';
		} else{
			this.fillColor = this.color;
		}
	}
	
	
	/**
	 * @desc 注释清除
	 */
	Annotation.prototype.clear = function() {
		var self = this;
        var shapeArray = self.shapeArray;
        var displayList = self.zr.storage.getDisplayList();
        var groupChildren = self.shapeGroup.children();

        for(var i = shapeArray.length - 1; i >= 0; i--){
			self.zr.remove(shapeArray[i].shape);
            
            for(var j = displayList.length - 1; j >= 0; j--){
                if(displayList[j].id == shapeArray[i].shape.id){
                    self.zr.remove(displayList[j])
                }
            }   

            for(var k = groupChildren.length - 1; k >= 0; k--){
                if(groupChildren[k].id == shapeArray[i].shape.id){
                    self.shapeGroup.remove(groupChildren[k])
                }
            }
		}

		self.shapeArray = [];
//		self.currentIndex = -1;
        self.undoArray = [];
        self.redoArray = [];

		
		self.roomHandle.sendRemoveAnnotation(self.boardId,shapeTypeEnum.clear,"")
	}
	

    /**
	 * @desc 注释撤销上一步
	 */
	Annotation.prototype.undo = function() {
		var self = this;

		if(self.undoArray.length < 1){
			return;
		}

		var undoObj = self.undoArray.pop();
		self.undoShape(undoObj);

	}

	/**
	 * @desc 执行撤销批注对象
	 */
	Annotation.prototype.undoShape = function(undoObj) {
		var self = this;
		switch (undoObj.type) {
			case 'delete': 
                self.shapeGroup.add(undoObj.shapeObj.shape);
				// 添加到shapeArray中
				self.shapeArray.push(undoObj.shapeObj);
				self.roomHandle.sendAddAnnotation(self.boardId, undoObj.shapeObj.shape, undoObj.shapeObj.shapeType, undoObj.shapeObj.color, undoObj.shapeObj.lineWidth, undoObj.shapeObj.arrowtype, undoObj.shapeObj.fillType);
                self.redoArray.push(undoObj);
				break;
			case 'add': 
				self.zr.remove(undoObj.shapeObj.shape);
                self.shapeGroup.remove(undoObj.shapeObj.shape);
				// 从shapeArrry中查找并删除
				deleteFromShapeArray(undoObj.shapeObj.shape.id);
				self.roomHandle.sendRemoveAnnotation(self.boardId,shapeTypeEnum.eraser,undoObj.shapeObj.shape.id);
                self.redoArray.push(undoObj);
				break;
			default:  
				break;
		}
		
		/**
		 * @desc 从批注数组中删除指定的批注
		 * @param {String} shapeId 批注的id
		 */
		function deleteFromShapeArray(shapeId) {
			if(self.shapeArray.length < 1){
				return;
			}
	
			for(var i = 0; i < self.shapeArray.length; i++){
				if(self.shapeArray[i].shape.id === shapeId){
					self.shapeArray.splice(i, 1);
				}
			}
		}
	}


	/**
	 * @desc 注释重做下一步
	 */
	Annotation.prototype.redo = function() {
		
		var self = this;
        // self.shapeGroup.add(obj.shapeObj.shape);
		// self.shapeArray.push(obj.shapeObj);
		// self.roomHandle.sendAddAnnotation(self.boardId, obj.shapeObj.shape, obj.shapeObj.shapeType, obj.shapeObj.color, obj.shapeObj.lineWidth, obj.shapeObj.arrowtype, obj.shapeObj.fillType);

        if(self.redoArray.length < 1){
			return;
		}

		var redoObj = self.redoArray.pop();
		self.redoShape(redoObj);
	}

    /**
	 * @desc 执行撤销批注对象
	 */
	Annotation.prototype.redoShape = function(redoObj) {
		var self = this;
		switch (redoObj.type) {
			case 'delete': 
                self.zr.remove(redoObj.shapeObj.shape);
                self.shapeGroup.remove(redoObj.shapeObj.shape);
                // 从shapeArrry中查找并删除
                deleteFromShapeArray(redoObj.shapeObj.shape.id);
                self.roomHandle.sendRemoveAnnotation(self.boardId,shapeTypeEnum.eraser,redoObj.shapeObj.shape.id);
                self.undoArray.push(redoObj);
				break;
			case 'add': 
                self.shapeGroup.add(redoObj.shapeObj.shape);
				// 添加到shapeArray中
				self.shapeArray.push(redoObj.shapeObj);
				self.roomHandle.sendAddAnnotation(self.boardId, redoObj.shapeObj.shape, redoObj.shapeObj.shapeType, redoObj.shapeObj.color, redoObj.shapeObj.lineWidth, redoObj.shapeObj.arrowtype, redoObj.shapeObj.fillType);
                self.undoArray.push(redoObj);
				break;
			default:  
				break;
		}
		
		/**
		 * @desc 从批注数组中删除指定的批注
		 * @param {String} shapeId 批注的id
		 */
		function deleteFromShapeArray(shapeId) {
			if(self.shapeArray.length < 1){
				return;
			}
	
			for(var i = 0; i < self.shapeArray.length; i++){
				if(self.shapeArray[i].shape.id === shapeId){
					self.shapeArray.splice(i, 1);
				}
			}
		}
	}
	
	/**
	 * @desc 设置激光笔
	 */
	Annotation.prototype.hlightPointInit = function(mouserUrl,width,height,intervalTime) {
		var self = this;
		self.hlightPointInfo.imgUrl = mouserUrl;
		self.hlightPointInfo.width = parseInt(width)?parseInt(width):self.hlightPointInfo.width;
		self.hlightPointInfo.height = parseInt(height)?parseInt(height):self.hlightPointInfo.height;
		self.hlightPointInfo.intervalTime = parseInt(intervalTime)?parseInt(intervalTime):self.hlightPointInfo.intervalTime;
	}
	/**
	 * @desc 清除激光笔
	 */
	Annotation.prototype.clearCurrHlightPoint = function() {
		var self = this;
		if(self.hlightPointShape){
			self.roomHandle.sendRemoveAnnotation(self.boardId,shapeTypeEnum.eraser,self.hlightPointShape.id)
			self.zr.remove(self.hlightPointShape);
            self.shapeGroup.remove(self.hlightPointShape);
			self.hlightPointShape = null;
		}
		if(self.textTagShape){
			self.roomHandle.sendRemoveAnnotation(self.boardId,shapeTypeEnum.eraser,self.textTagShape.id)
			self.zr.remove(self.textTagShape);
            self.shapeGroup.remove(self.textTagShape);
			self.textTagShape = null;
		}
	}
	
	/**
	 * @desc 设置激光笔
	 */
	Annotation.prototype.startHlightPoint = function(){
		var self = this;
		self.setShapeType(shapeTypeEnum.hlight_point)
		var width = self.hlightPointInfo.width;
		var height = self.hlightPointInfo.height;
		self.hlightPointShape = new zrender.Image({
			position: [-100, -100],
            scale: [1, 1],
            style: {
                image: self.hlightPointInfo.imgUrl,
                width: self.hlightPointInfo.width,
                height: self.hlightPointInfo.height
            },
            draggable: true
		})
		
        self.shapeGroup.add(self.hlightPointShape);
		self.hlightPointShape.id = Math.uuid();
		self.renderDiv.onmousemove = function(e){

			if(self.isAllowEditImg){
				return;
			}
				
			if(self.shapeType != shapeTypeEnum.hlight_point){
				return;
			}

            var tempPos = self.handleScaledXY(e.clientX - this.getBoundingClientRect().left, e.clientY - this.getBoundingClientRect().top);
			x = tempPos.x;
			y = tempPos.y;
			var realX = x - width / 2;
			var realY = y - height / 2;
			self.hlightPointShape.attr('position',[realX ,realY]);
			if(self.hlightPointInfo.isSend){
				self.hlightPointInfo.isSend = false;
				
				self.roomHandle.sendAddAnnotation(self.boardId, self.hlightPointShape, shapeTypeEnum.hlight_point, self.color, self.lineWidth, self.arrowtype, self.fillType);
				setTimeout(function(){
					self.hlightPointInfo.isSend = true;
				}, self.hlightPointInfo.intervalTime)
			}
		}
	}
	
	
	
	Annotation.prototype.changeScreen = function(newWidth,newHeight){
		var self = this;
		if(self.zr && self.shapeArray.length > 0){
			// self.zr.resize();
            self.boardReRenderWH(newWidth, newHeight);
			for(var k in self.shapeArray){
				var x = newWidth/self.screenSize.width;
				var y = newHeight/self.screenSize.height;
				console.log("===Annotation.changeScreen(),x:"+x+",y:"+y);
				self.shapeArray[k].shape.animateTo({
					scale: [x, y]
				},function(){
					console.log('changeScreen');
				})
			}
		}
		self.screenSize.width = newWidth;
		self.screenSize.width = newHeight;
	}
	
	
    /**
     * @desc 上传图片
     * @param {String} accessToken 认证串
     * @param {object} fileTarget input:file控件选择的图片文件对象
     * @returns {object} Promise
     */
	Annotation.prototype.uploadImg = function(accessToken, fileTarget){
		var deferred = when.defer();
		var self = this;
		var file = fileTarget.files[0];
        
		if (!file){
			var error = new Error(ErrorConstant.upload_filer_required);
			deferred.reject(error);
		}
		
		// 最大上传文件大小
		var MAX_SIZE = 15 * 1024 * 1024;
		
		// 上传文件类型
		var FILE_TYPES = ['image/jpeg', 'image/png'];
		
		var fileSize = file.size,
            fileType = file.type;
        
		
		//console.log("=====================file:",file);
		
		log.info('===annotation.uploadImg(),fileSize:'+ fileSize+", fileType"+ fileType);
		
		if (!FILE_TYPES.includes(fileType)) {
			fileTarget.value = '';
		    var error = new Error(ErrorConstant.upload_filer_fileType);
			error.message = error.message+",Support jpeg/jpg/png only ";
		    deferred.reject(error);
			
		}else if(fileSize > MAX_SIZE) {
		   fileTarget.value = '';
		   var error = new Error(ErrorConstant.upload_filer_fileSize);
		   error.message = error.message+",Within 15M only";
		   deferred.reject(error);
		   
		}else {
			var restServer = new RestServer();
                restServer.uploadFile(accessToken, file).then(function(fileUrl){
                    fileTarget.value = '';
                    log.info("===annotation.uploadImg(),fileUrl:" + fileUrl);
                    var img = new Image();
                    img.src = fileUrl;
                    img.onload = function(){
                        var imgWidth = img.width;
                        var imgHeight = img.height;
                        log.info("===annotation.uploadImg(),img width:" + imgWidth+", height:"+ imgHeight);
                        
                        self.drawImg(fileUrl,imgWidth, imgHeight);
                        
                        img = null;
                        deferred.resolve();
                    }
                }).otherwise(function(error){
                   console.info("===annotation.uploadImg(),error:",error);
                   deferred.reject(error);
                });
		}
		
		return deferred.promise;
	}
	
    // 本端绘制图片(不包括本端关闭白板后再重新打开白板后,重新渲染自己的图片不会走这里)
	Annotation.prototype.drawImg = function(imgUrl,imgWidth, imgHeight){
		log.info("===annotation.drawImg(),imgUrl:"+imgUrl+", imgWidth:" + imgWidth+", imgHeight:"+ imgHeight);
	  
		var MAX_WIDTH = 150;
		var MAX_HEIGHT = 150;
		var MAX_WH_RATIO = MAX_WIDTH / MAX_HEIGHT;
		
	     var originWHRatio = imgWidth / imgHeight;
	     var targetWidth = imgWidth;
	     var targetHeight = imgHeight;
	  
	     if (imgWidth > MAX_WIDTH || imgHeight > MAX_HEIGHT) {
			  if (originWHRatio > MAX_WH_RATIO) {
				targetWidth = MAX_WIDTH;
				targetHeight = Math.round(targetWidth / originWHRatio);
			  } else {
				targetHeight = MAX_HEIGHT;
				targetWidth = Math.round(targetHeight * originWHRatio);
			  }
	    }
		log.info("===annotation.drawImg(),targetWidth:"+targetWidth+", targetHeight:" + targetHeight);
		
		var self = this;

        self.shapeType = shapeTypeEnum.img;
        
        var img = new zrender.Image({
		    style: {
				image:imgUrl,
		        x: 0,
		        y: 0,
		        width: targetWidth,
				height: targetHeight,
				shadowColor: 'red',
				shadowBlur: 5
		    },
			zlevel: self.imgZlevel, //zlevel 大的 Canvas 会放在 zlevel 小的 Canvas 的上面。
			id: Math.uuid(),
            rotation: 0,
            originX: 0,
            originY: 0
		});
		
	
        var shapeObj = {
            shape:img,
            shapeType:self.shapeType,
            color:self.color,
            lineWidth:self.lineWidth,
            arrowtype:self.arrowtype,
            fillType:self.fillType,
        }

        self.shapeArray.push(shapeObj);
        self.shapeGroup.add(img);
        self.undoArray.push({
            type: 'add',
            shapeObj: shapeObj
        })
	    
	    self.setAllowEditImg(true);
	    img.attr('zlevel',++self.imgZlevel);
	    self.currActiveImg = img;

		
	    self.shapeGroup.eachChild(function(item){
	    	if(item.type === 'image'){
                if(item.id == self.currActiveImg.id){
                    item.attr('style',{shadowColor:'red',shadowBlur:5});
                }else{
                    item.attr('style',{shadowColor:undefined,shadowBlur:undefined});
                }
            }
	    });

		self.roomHandle.sendAddAnnotation(self.boardId, img, shapeTypeEnum.img, self.color, self.lineWidth, self.arrowtype, self.fillType);
	}
	
	/**
     * @desc 设置图片的编辑状态
     * @param {Boolean} isAllow 白板是否设置为图片模式,在此模式下画笔不生效,设置画笔时请关闭图片模式
     */
	Annotation.prototype.setAllowEditImg = function(isAllow){
		var self = this;
		if(isAllow == undefined){
		   	this.isAllowEditImg = !this.isAllowEditImg;
		}else{
			this.isAllowEditImg = isAllow;

		}

        this.shapeGroup.eachChild(function(child){
            if(child.type && child.type !== 'image'){
                child.attr('silent', self.isAllowEditImg)
            }
        })
		
		if(!this.isAllowEditImg){
			this.currActiveImg = null;
		}else{
            this.setShapeType(shapeTypeEnum.img)
        }
		
		this.shapeGroup.eachChild(function(item){
		    if(item.type === 'image'){
                if(!self.isAllowEditImg){
                    item.attr('style',{shadowColor:undefined,shadowBlur:undefined});
                }
            }
		})

        if(this.textInfo && this.shapeType === shapeTypeEnum.txt){
            this.textInfo.setTextEditEnable(!this.isAllowEditImg)
        }
	}
	
	Annotation.prototype.getAllowEditImg = function(){
		return this.isAllowEditImg;
	}
	
	/**
     * @desc 缩放当前高亮的图片
     * @param {boolean} isEnlarge 是否放大,true为放大,false为缩小
     * @param {number} percentage 缩放比例,小数,例如0.1
     */
	Annotation.prototype.scaleImg = function(isEnlarge, percentage){
		var self = this;
		if(!this.isAllowEditImg){
			return;
		}
		
		if(!this.currActiveImg){
			return;
		}
		
		var x = this.currActiveImg.scale[0];
		var y = this.currActiveImg.scale[1];
		if(isEnlarge){
			x = x + percentage;
			y = y + percentage;
		}else{
            // 防止x或者y为0后,继续缩小变为负数,导致放大和缩小效果相反了
            if(x - percentage <= 0.1 || y - percentage <= 0.1){
                return;
            }
			x = x - percentage;
			y = y - percentage;
		}
		//设置缩放大小
		this.currActiveImg.attr('scale',[x,y]);
		
		//设置缩放中心
		this.currActiveImg.attr('origin',[this.currActiveImg.style.width/2, this.currActiveImg.style.height/2]);
        
        if(isEnlarge){
			this.currActiveImg.customPositionX = this.currActiveImg.customPositionX - this.currActiveImg.style.width/2 * percentage;
			this.currActiveImg.customPositionY = this.currActiveImg.customPositionY - this.currActiveImg.style.height/2 * percentage;
		}else {
			this.currActiveImg.customPositionX = this.currActiveImg.customPositionX + this.currActiveImg.style.width/2 * percentage;
			this.currActiveImg.customPositionY = this.currActiveImg.customPositionY + this.currActiveImg.style.height/2 * percentage;
		}
		
		
		self.roomHandle.sendUpdateAnnotation(this.boardId, this.currActiveImg, shapeTypeEnum.img);
	}
	
	/**
     * @desc 顺时针旋转当前高亮的图片
     */
	Annotation.prototype.rotateImg = function(){
		var self = this;
		if(!this.isAllowEditImg){
			return;
		}
		
		if(!this.currActiveImg){
			return;
		}
		
		//设置旋转角度
		var rotation = this.currActiveImg.rotation - Math.PI/2;
		this.currActiveImg.attr('rotation',[rotation]);
		
		//设置旋转中心
		this.currActiveImg.attr('origin',[this.currActiveImg.style.width/2, this.currActiveImg.style.height/2]);
	  
		self.roomHandle.sendUpdateAnnotation(this.boardId, this.currActiveImg, shapeTypeEnum.img);
	}
	
	/**
     * @desc 删除当前高亮的图片
     */
	Annotation.prototype.delImg = function(){
		if(!this.isAllowEditImg){
			return;
		}
		
		if(!this.currActiveImg){
			return;
		}

		
		this.roomHandle.sendRemoveAnnotation(this.boardId, shapeTypeEnum.img, this.currActiveImg.id);
		this.shapeGroup.remove(this.currActiveImg);
        
        for(var i = 0; i < this.shapeArray.length; i++){
            var tempShapeObj = this.shapeArray[i];
            this.undoArray.push({
                type: 'delete',
                shapeObj: tempShapeObj
            })
            if(tempShapeObj.shape && (tempShapeObj.shape.id == this.currActiveImg.id)){
                this.shapeArray.splice(i, 1);
            }
        }

        this.currActiveImg = null;
	}

    Annotation.prototype.handleScaledXY = function(x, y) {
        var pos = {
            x: x,
            y: y
        }

        if(this.positionInfo.scaleRatioX == 1 && this.positionInfo.scaleRatioX == 1){
            return pos
        }

        if(this.positionInfo.scaleRatioX > 1){
            pos.x /= this.positionInfo.scaleRatioX
        }else if(this.positionInfo.scaleRatioX < 1){
            pos.x *= this.positionInfo.scaleRatioXR
        }

        if(this.positionInfo.scaleRatioY > 1){
            pos.y /= this.positionInfo.scaleRatioY
        }else if(this.positionInfo.scaleRatioY < 1){
            pos.y *= this.positionInfo.scaleRatioYR
        }

        return pos
    }

    /**
     * @description 根据宽高重新渲染画布及画布内的所有批注
     * @param {int} width 宽  
     * @param {int} height 高
     */
    Annotation.prototype.boardReRenderWH = function (width, height) {

        
        /**
         * 在执行缩放时,每次都还原到初始大小再进行缩放,每次都在最开始的宽高进行缩放
         * 防止:先放大再缩小后点击位置偏移
         */
        if(width !== this.positionInfo.originalWidth || height !== this.positionInfo.originalHeight){
            console.log('===scale on based position before scale')
            this.boardReRenderWH(this.positionInfo.originalWidth, this.positionInfo.originalHeight)
        }

        var currWidth = this.zr.getWidth();
        var currHeight = this.zr.getHeight();

        log.debug('===boardReRenderWH, width: '+ width + 'height: ' + height + ' currWidth: ' + currWidth + ' currHeight: ' + currHeight)

        
        var tempInfo = {
            scaleWidth: width,
            scaleHeight: height,
            zrWidth: currWidth,
            zrHeight: currHeight,
            scaleRatioX: width / currWidth,
            scaleRatioY: height / currHeight,
            scaleRatioXR: currWidth / width,
            scaleRatioYR: currHeight / height,
        }
        
        if(width === this.positionInfo.originalWidth && height === this.positionInfo.originalHeight){
            tempInfo.scaleRatioXR = 1;
            tempInfo.scaleRatioYR = 1;
        }

        Object.assign(this.positionInfo, tempInfo)
        
        if(this.positionInfo.scaleRatioX == 1 && this.positionInfo.scaleRatioY==1){
            return;
        }

        // 以当前shapeGroup的缩放为基础更新缩放
        this.positionInfo.scaleRatioX *= this.shapeGroup.scaleX;
        this.positionInfo.scaleRatioY *= this.shapeGroup.scaleY;

        this.zr.resize({
            width: width,
            height: height
        });

        this.zr.dom.style.width = width +'px';
        this.zr.dom.style.height = height +'px';

        if(this.renderBackground){
            this.renderBackground.width = width;
            this.renderBackground.height = height;
            this.renderBackground.style.width = width +'px';
            this.renderBackground.style.height = height +'px';
            if(this.board.backgroundColor){
                this.updateBackgroundColor(this.board.backgroundColor);
            }
            if(this.board.backgroundImage){
                this.updateBackgroundImage(this.board.backgroundImage);
            }
        }

        this.shapeGroup.attr({
            scaleX: this.positionInfo.scaleRatioX,
            scaleY: this.positionInfo.scaleRatioY,
            originX: 0,
            originY: 0
        })

        log.debug('===Annotation boardReRenderWH positionInfo: ', this.positionInfo);
    }

    /**
     * @description 开始输入文字
     * @param {object} themeConfig 主题样式,用于自定义文字输入框确认和取消按钮,例如:{confirm: {color: '#000000', text: '确定'}, cancel: {color: '#001100', text: '取消'}}
     */
    Annotation.prototype.startTextInput = function(themeConfig) {
        var self = this;
        self.setShapeType(shapeTypeEnum.txt)
        if(this.isAllowEditImg){
            // 当前为图片编辑模式或者已经初始化过textInfo了,就跳过
            return
        }
        if(!this.isAllowEditImg && this.textInfo != null && this.textInfo.currTextElParentNode != null){
            this.textInfo.setTextEditEnable(true);
            return;
        }
        this.textInfo = new TextInfo(themeConfig, this);
        this.setShapeType(shapeTypeEnum.txt);
        // 判断父元素是否是canvas,如果是canvas则将工具条添加到canvas的兄弟节点
        this.textInfo.setParentElement(this.annoDiv);
        this.textInfo.setCanvasParentElement(this.annoDiv.tagName === 'CANVAS' ? this.annoDiv.parentElement : this.annoDiv);
        this.textInfo.initToolBar();
        this.textInfo.setColorChoosePanelDisplay(false);
        this.textInfo.setTextEditEnable(true);
        this.textInfo.setZrender(self.zr);
        this.textInfo.textEventEmitter.on('confirm', function(textInfo){
            renderByZrender(textInfo)
        })
        log.debug('===Annotation.startTextInput success');

        function renderByZrender(textInfo) {
            var newPos = self.handleScaledXY(textInfo.position[0], textInfo.position[1]);
            var textShape = new zrender.Text({
                style: {
                    text: textInfo.content,
                    fontSize: textInfo.fontSize,
                    fontStyle: textInfo.fontStyle,
                    fontWeight: textInfo.fontWeight,
                    fill: textInfo.fontColor || '#000000' // 添加默认颜色
                },
                zlevel: self.normalZlevel,
                position: [newPos.x, newPos.y],
                origin: [newPos.x, newPos.y - 50],
                onmouseover: mouseOverForEraser
            })
            textShape.id = Math.uuid();
			
            self.shapeGroup.add(textShape);
    
            var shapeObj = {
                shape:textShape,
                shapeType:self.shapeType,
                color:self.color,
                lineWidth:self.lineWidth,
                arrowtype:self.arrowtype,
                fillType:self.fillType,
            }
            self.shapeArray.push(shapeObj);
            self.roomHandle.sendAddAnnotation(self.boardId, textShape, shapeTypeEnum.txt, self.color, self.lineWidth, self.arrowtype, self.fillType);
        }
    
        function mouseOverForEraser(params) {
            if(self.shapeType == shapeTypeEnum.eraser && self.isEraser){
                for(var k in self.shapeArray){
                    if(self.shapeArray[k].shape.id == params.target.parent.id){
                        self.zr.remove(params.target.parent)
                        self.shapeGroup.remove(params.target.parent)
                        self.roomHandle.sendRemoveAnnotation(self.boardId,self.shapeType,self.shapeArray[k].shape.id)
                        self.undoArray.push({
                            type: 'delete',
                            shapeObj: self.shapeArray[k]
                        })
                        self.shapeArray.splice(k,1);
                    }
                }
            }
        }
    }
	return Annotation;
});