[javascript]canvas 热点区域图,框选区域并在区域显示热点

2023-11-10

需求:

设置人员需要在图片上框选区域,并且后台针对区域读出数据后显示热点。

做法:

直接上代码:

/*****
 * 作者:jww_dragon@163.com
 * 依赖: jquery
 * 
 ********************/

var goog = function(tag) {
	this._hotpoint = [];
	this._square = {};
	if (typeof tag === "string") {
		this.canvas = document.getElementById(tag);
	} else {
		this.canvas = tag;
	}
	this.context = this.canvas.getContext('2d');
	this.colorStyle = {};
	this.colorStyle.selectColor = "rgba(255,0,0,0.8)";
	this.colorStyle.selectedColor = "rgba(255,0,0,0.3)";
	this.colorStyle.showColorBG = "rgba(0,0,0,0.2)";
	this.colorStyle.shadowColor = "#000000";
	this.colorStyle.fontStyle = "16px Arial Black";
	this.colorStyle.fontColor = "yellow";
	this.context.shadowOffsetX = 5;
	this.context.shadowOffsetY = 5;
	this.context.shadowBlur = 10;
}

/**
 *换一个id监听(一般用不到)
 **/
goog.prototype.listen = function(documentId) {
	this.canvas = document.getElementById(documentId);
	this.canvas = document.getElementById(documentId);
	this.context = this.canvas.getContext('2d')
}

/**
 *颜色配置
 * selectColor,selectedColor 框选颜色,默认红色, showColorBG 展示的背景蒙层色
 * shadowColor 影阴颜色,fontStyle 字体,fontColor 字体颜色
 **/
goog.prototype.setColor = function(type, color) {
	if (typeof color != "undefined" && type != "undefined") {
		this.colorStyle[type] = color;
	}
}

/**
 * 以下是选择部分
 ***/
goog.prototype.init_mask = function() {
	var position_top = this.canvas.offsetTop;
	var position_left = this.canvas.offsetLeft;
	this.temp_canvas = document.createElement("canvas");
	this.temp_canvas.setAttribute('width', this.canvas.width);
	this.temp_canvas.setAttribute('id', "mask-gogo123");
	this.temp_canvas.setAttribute('height', this.canvas.height);
	this.temp_canvas.setAttribute('style', 'position: absolute;left: '
			+ position_left + 'px;top: ' + position_top + 'px;');
	this.canvas.parentNode.appendChild(this.temp_canvas);
	//return temp_canvas;
}
goog.prototype.drowInitSquare = function(_list) {
	this.context.shadowColor = this.colorStyle.shadowColor;
	this.context.font = this.colorStyle.fontStyle;
	//this.context.strokeStyle=this.colorStyle.fontColor;
	for ( var _i in _list) {
		this.context.fillStyle = this.colorStyle.selectedColor;
		this.context.fillRect(_list[_i].x, _list[_i].y, _list[_i].w,
				_list[_i].h);
		//这里是业务需求eventName现示出来
		if (!!_list[_i].eventName) {
			this.context.fillStyle = this.colorStyle.fontColor;
			var font_w = (_list[_i].w - this.context
					.measureText(_list[_i].eventName).width) / 2;
			if (font_w < 2)
				font_w = 2;
			this.context.fillText(_list[_i].eventName, _list[_i].x + font_w,
					(_list[_i].y + _list[_i].h / 2 + 4), _list[_i].w - 5);
		}
	}
	this.context.closePath();
}

goog.prototype.selectDetermine = function(opt) {
	if (typeof this.temp_canvas.tempSquare == "undefined") {
		return;
	}
	var select_condtion = {};
	if (typeof opt != "undefined") {
		$.extend(select_condtion, opt);
	}
	this._square = this.temp_canvas.tempSquare;
	this.context.shadowColor = this.colorStyle.shadowColor;
	this.context.fillStyle = this.colorStyle.selectedColor;
	this.context.fillRect(this._square.x, this._square.y, this._square.w,
			this._square.h);
	this.context.closePath();

	//要用到jquery的就这么一处,如果实在不要jquery就自己写继承方法
	$.extend(select_condtion, this._square);
	//可以不要的
	select_condtion.uuid = this._hotpoint.length + "_"
			+ Math.floor(Math.random() * 10000);
	this._hotpoint.push(select_condtion);
	var k = "Coordinate range is generated:" + this._square.x + ','
			+ this._square.y + ',' + (this._square.x + this._square.w) + ','
			+ (this._square.y + this._square.h);
	console.log(k);
}
/**
 * 这个很重要,找了好久,才看见有个老外的回答,不然坐标会偏移
 */
function goog_findPos(obj) {
	var curleft = 0, curtop = 0;
	if (obj.offsetParent) {
		do {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		} while (obj = obj.offsetParent);
		return {
			x : curleft,
			y : curtop
		};
	}
	return undefined;
}

goog.prototype.selectListen = function() {
	this.init_mask();
	var temp_context = this.temp_canvas.getContext('2d');
	temp_context.strokeStyle = this.colorStyle.selectColor;
	var genCoordinate = this.genCoordinates;
	var f1 = {}, f2 = {};
	this.temp_canvas.onmousedown = function(_e) {
		var pos = goog_findPos(this);
		f1.layerX = _e.pageX - pos.x;
		f1.layerY = _e.pageY - pos.y;
		this.onmousemove = function(e) {
			temp_context.clearRect(0, 0, this.width, this.height);
			f2.layerX = e.pageX - pos.x;
			f2.layerY = e.pageY - pos.y;
			var square = genCoordinate(f1, f2);
			temp_context.roundRect(square.x, square.y, square.w, square.h);
		};
		this.onmouseup = function(e) {
			this.onmousemove = null;
			this.onmouseup = null;
			f2.layerX = e.pageX - pos.x;
			f2.layerY = e.pageY - pos.y;
			this.tempSquare = genCoordinate(f1, f2);
		};
	}
}

goog.prototype.getSelected = function() {
	return this._hotpoint;
}

/**
 * 以下是展示部分
 ***/
goog.prototype.setHotpoint = function(_datas) {
	if (typeof _datas != "undefined") {
		this._hotpoint = _datas;
	}
}
goog.prototype.showListen = function() {
	this.addShowListen(this._hotpoint);
}
goog.prototype.addShowListen = function(_datas) {
	this.canvas.context = this.context;
	this.canvas.googColorStyle = this.colorStyle;
	var _show = this.show;
	var _showDiv = this.showDiv;
	this.goog_DataFormat(_datas);
	this.createDiv(this.canvas, _datas);
	var f = {};
	this.canvas.onmouseover = function(e) {
		this.context.fillStyle = this.googColorStyle.showColorBG;
		this.context.shadowColor = this.googColorStyle.shadowColor;
		this.context.fillRect(0, 0, this.width, this.height);
		_show(this.context, _datas);
	};
	this.canvas.onmouseout = function(e) {
		this.context.closePath();
		this.context.clearRect(0, 0, this.width, this.height);
	};
	this.canvas.onmousemove = function(e) {
		var pos = goog_findPos(this);
		f.layerX = e.pageX - pos.x;
		f.layerY = e.pageY - pos.y;
		if (this.context.isPointInPath(f.layerX, f.layerY)) {
			for ( var cirle in _datas) {
				var amt = _datas[cirle];
				if (f.layerX > Math.min(amt.x, amt.x + amt.w)
						&& f.layerX < Math.max(amt.x, amt.x + amt.w)
						&& f.layerY > Math.min(amt.y, amt.y + amt.h)
						&& f.layerY < Math.max(amt.y, amt.y + amt.h)) {

					$("#goog_poop_" + amt.uuid).show();
				}
			}
		} else {
			$('.goog_tag_v').hide();
		}
	};
}
/**
 *用来现实展示具体值的DIV
 ***/
goog.prototype.createDiv = function(canvas, _datas) {
	$('.goog_tag_v').remove();
	var position_top = canvas.offsetTop;
	var position_left = canvas.offsetLeft;
	for ( var i in _datas) {
		var amt = _datas[i];
		var _tag = document.createElement("div");
		_tag.setAttribute('id', "goog_poop_" + amt.uuid);
		_tag.setAttribute('class', "goog_tag_v");
		_tag.setAttribute('width', 300);
		_tag.setAttribute('height', 50);
		_tag.setAttribute('style',
				'display:none;background:#000; color:#FFF; position: absolute;left: '
						+ (position_left + amt.x) + 'px;top: '
						+ (position_top + amt.y) + 'px;');
		_tag.innerText = "数量:" + amt.data;
		canvas.parentNode.appendChild(_tag);
		//或则加名字什么的
	}

}

goog.prototype.destroy = function() {
	this.canvas.remove();
	if (typeof this.temp_canvas != "undefined") {
		this.temp_canvas.remove();
	}
	this._hotpoint = [];
	this._square = {};
}

goog.prototype.showListenDestroy = function() {
	this.canvas.onmousemove = null;
	this.canvas.onmouseover = null;
	this.canvas.onmouseout = null;
}

//预留,万一要转换数组型到类对象
goog.prototype.goog_DataFormat = function(_datas) {
	if (_datas.length <= 0) {
		return;
	}
	var sum = 0
	
	for ( var _i in _datas) {
		if (typeof _datas[_i].x == "undefined") {
			//预留,万一要转换数组型到类对象
			return;
		}
		//业务
		_datas[_i].data = _datas[_i].eventAmount;
		if (typeof _datas[_i].data == "undefined") {
			//DEMO 用
			_datas[_i].data = Math.random() * 100;
		}
		sum = sum + _datas[_i].data;
	}
	var avg = sum / _datas.length;
	//分段函数,看情况,这是业务逻辑了
	for ( var _i in _datas) {
		if (_datas[_i].data / avg > 1.5) {
			_datas[_i].type = 1;
		} else if (_datas[_i].data / avg >= 1) {
			_datas[_i].type = 2;
		} else if (_datas[_i].data / avg >= 0.5) {
			_datas[_i].type = 3;
		} else {
			_datas[_i].type = 4;
		}
	}
}

goog.prototype.show = function(context, _datas) {
	if (typeof _datas == "undefined") {
		return;
	}
	if (typeof _datas == null) {
		return;
	}
	if (_datas.length <= 0) {
		return;
	}
	for ( var _i in _datas) {
		cirle = _datas[_i];
		var r = Math.min(cirle.h, cirle.w) / 2;
		r = Math.min(r, 50); //最大半径为50 ,以后半径可以和data的大小有关
		//中心点确定.渐变
		my_gradient = context.createRadialGradient((cirle.x + cirle.w / 2),
				(cirle.y + cirle.h / 2), 0, (cirle.x + cirle.w / 2),
				(cirle.y + cirle.h / 2), r);
		if (cirle.type == 1) {
			my_gradient.addColorStop(0.0, "rgba(255,0,0,0.8)"); //定义红色渐变色
			my_gradient.addColorStop(0.3, "rgba(255,255,0,0.7)"); //定义黄色渐变色
			my_gradient.addColorStop(0.6, "rgba(0,255,0,0.5)"); //定义绿色渐变色
			my_gradient.addColorStop(0.9, "rgba(0,0,255,0.2)"); //定义蓝色渐变色	      
		} else if (cirle.type == 2) {
			my_gradient.addColorStop(0.3, "rgba(255,255,0,0.7)"); //定义黄色渐变色
			my_gradient.addColorStop(0.6, "rgba(0,255,0,0.5)"); //定义绿色渐变色
			my_gradient.addColorStop(0.9, "rgba(0,0,255,0.3)"); //定义蓝色渐变色
		} else if (cirle.type == 3) {
			my_gradient.addColorStop(0.2, "rgba(0,255,0,0.5)"); //定义绿色渐变色
			my_gradient.addColorStop(0.7, "rgba(0,0,255,0.4)"); //定义蓝色渐变色
		} else {
			my_gradient.addColorStop(0.3, "rgba(0,0,255,0.6)"); //定义蓝色渐变色
		}

		my_gradient.addColorStop(1, "rgba(0,0,0,0)"); //定义黑色渐变色
		context.fillStyle = my_gradient;
		context.arc((cirle.x + cirle.w / 2), (cirle.y + cirle.h / 2), r, 0,
				Math.PI * 2, true);
		context.fill();
	}
}

/**
 * 以下是工具类
 ***/
//必须保证在第三象限,不然后面的都要出错。
goog.prototype.genCoordinates = function(_e, e) {
	var square = {};
	var x = _e.layerX;
	var x2 = e.layerX;
	var y = _e.layerY;
	var y2 = e.layerY;
	var w = x2 - x;
	var h = y2 - y;
	if (w < 0) {
		w = 0 - w;
		x = x2;
	}
	if (h < 0) {
		h = 0 - h;
		y = y2;
	}
	square.x = x;
	square.y = y;
	square.w = w;
	square.h = h;
	return square;
}

//圆弧函数,补充的,也可以用方块函数/
CanvasRenderingContext2D.prototype.roundRect = function(x, y, width, height,
		radius, stroke) {
	if (typeof stroke == "undefined") {
		stroke = true;
	}
	if (typeof radius === "undefined") {
		radius = 5;
	}
	this.beginPath();
	this.moveTo(x + radius, y);
	this.lineTo(x + width - radius, y);
	this.quadraticCurveTo(x + width, y, x + width, y + radius);
	this.lineTo(x + width, y + height - radius);
	this
			.quadraticCurveTo(x + width, y + height, x + width - radius, y
					+ height);
	this.lineTo(x + radius, y + height);
	this.quadraticCurveTo(x, y + height, x, y + height - radius);
	this.lineTo(x, y + radius);
	this.quadraticCurveTo(x, y, x + radius, y);
	this.closePath();
	if (stroke) {
		this.stroke();
	}
};


测试使用,以及例子

其实这里的框选和显示是可以分开到2个页面的。
  <body>
    <div class="container">
	<div>
	<!--
	<button type="button" οnclick="c1()">不要按1</button>
	<button type="button" οnclick="c2()">不要按2</button>
	-->
	<button type="button" οnclick="c3()">确认按钮</button>
	图片上单击框选-->点击确认-->下面一张图察看效果.
	</div> 
	<div style="margin-left: 100px;margin-top: 100px;">
	<canvas id="tt" width="705" height="400"  style="background: url(d.jpg) no-repeat center center;position: absolute;left: 0px;top: 50px;"></canvas>
	<canvas id="show" width="705" height="400"  style="background: url(d.jpg) no-repeat center center;position: absolute;left: 0px;top: 460px;"></canvas>
	</div>	
      </div> 
  </body>


<script type="text/javascript">
      var a = new goog("tt");
      var b = new goog("show");
      a.selectListen();
      function c3() {
	a.selectDetermine();
	var dat = a.getSelected();
	b.addShowListen(dat);
      }
</script>

效果


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[javascript]canvas 热点区域图,框选区域并在区域显示热点 的相关文章

随机推荐

  • python熵权法过程中,权重出现nan值问题

    最近在利用熵权法选取最优指标数据时 计算权重得到的是全为nan值的权重 经过分析过程 找到问题所在 数据展示 熵权法步骤 step 1 标准化处理 step 2 计算每个维度的信息熵 step 3 差异系数 step 4 计算权重 step
  • Altium Designer 21的使用(二):电阻电容模型的创建

    TIPS 元件符号是元件在原理图上的表现形式 主要由元件边框 管脚 包括管脚序号和管脚名称 元件名称及元件说明组成 通过放置的管脚来建立电气连接关系 元件符号中的管脚序号是和电子元件实物的管脚一一对应的 在创建元件时 图形不一定和实物完全一
  • java io流读取文件_java的几种IO流读取文件方式

    一 超类 字节流 InputStream 读入流 OutputStream 写出流 字符流 Reader 字符 读入流 Writer 字符写出流 二 文件操作流 字节流 FileInputStream FileOutputStream 字符
  • tensorflow码源-运行流程

    tensorflow码源 运行流程 简介 通过分析用户构建的计算是如何在tensorflow中运行的 了解tensorflow中的基本元素和op kernel和device之间的交互 用户程序 matrix1 tf constant 3 3
  • 如何实现‘请在微信客户端打开链接’

    想要实现请在微信客户端打开链接 在代码中加入以下代码即可 code style font family none display block line height 18px border none code
  • 【编程之路】面试必刷TOP101:动态规划(72-77,Python实现)

    面试必刷TOP101 动态规划 72 77 Python实现 72 连续子数组的最大和 小试牛刀 72 1 动态规划 因为数组中有正有负有0 因此每次遇到一个数 要不要将其加入我们所求的连续子数组里面 是个问题 有可能加入了会更大 有可能加
  • js阻止冒泡事件

    div class open div style width 50 margin 0 auto height 5rem div class open back img style width 2 25rem src image public
  • JAVA switch case 穿透问题

    1 前提 其实开发中很少会用到switch 一般更倾向于if else 但是最近接手的项目 前人写的代码都用switch 但是我一直以来对switch 的理解就跟if一样 然后项目运用的时候才发现这玩意居然还有穿透问题 2 实践 publi
  • 米家扩展程序初始化超时,GCC编译器警告:扩展初始化程序列表仅在c ++ 0x中可用...

    Using this member initialization StatsScreen StatsScreen GameState State level m Level level I get the following warning
  • Eclipse新版本注释的中文大小不一,缩进有问题. Eclipse新版本的坑

    notepad 可以关闭打开标签 左边所有 右边所有 而eclipse的旧版本却没有 就去找了新版本的Eclipse 来用 结果就踩坑了 Eclipse IDE 2020 09 需要jdk11 Eclipse IDE 2020 06 可以用
  • shiro多项目跳转用户身份失效问题排查

    shiro多项目跳转用户身份失效问题排查 1 身份失效问题 最近在项目中遇到过一个问题 统一登录系统中有各个子系统的模块 可点击子系统模块进行跳转 如下图所示 如上图 当用户点击子系统B新窗口打开时 实现跳转成功 当再回到原统一登录系统页面
  • iocrl如何从user space调用到 kernel space,

    iocrl如何从user space调用到 kernel space 还有调用的流程 图1 在上述的调用流程中 do vfs ioctl 会处理一些内核自定义的cmd type 如果我们自定义的cmd type和系统定义的重复 会导致 该自
  • SQL 测试

    您的回答 1 SQL 指的是 您的回答 Structured Query Language 2 哪个 SQL 语句用于从数据库中提取数据 您的回答 SELECT 3 哪条 SQL 语句用于更新数据库中的数据 您的回答 UPDATE 4 哪条
  • LintCode之128 哈希函数

    题目来源 哈希函数 题目描述 在数据结构中 哈希函数是用来将一个字符串 或任何其他类型 转化为小于哈希表大小且大于等于零的整数 一个好的哈希函数可以尽可能少地产生冲突 一种广泛使用的哈希函数算法是使用数值33 假设任何字符串都是基于33的一
  • 一文解释python中的实例方法,类方法和静态方法作用和区别是啥?该如何使用

    我们都知道 python类中有三种常见的方法 分别是实例方法 类方法和静态方法 那么这几个方法到底有什么作用 它们之间有什么区别 该如何使用 带着这些问题 下面我们就来了解下这三种方法 1 方法介绍 2 代码说明 若我们要实现一个学生的类
  • ==,Object.equals(), Objects.equals(), Objects.deepEquals 使用注意

    相等判断时需注意 基本数据类型之间 可使用 包装类型之间尽量不要使用 包装类数值超过 127 128 引用类型地址不同 在 127 128之间时 翻阅底层源码发现 使用的是缓存的值 使用Object equals 做内容比较时 把常量 或可
  • IM即时通讯实现原理

    即时通讯 Instant Messenger 简称IM 软件多是基于TCP IP和UDP进行通讯的 TCP IP和UDP都是建立在更低层的IP协议上的两种通讯传输协议 前 者是以数据流的形式 将传输数据经分割 打包后 通过两台机器之间建立起
  • Spyder 快捷键大全(转)

    主要介绍了Spyder常见的快捷键 从spyder的配置表中导出 中文名称为直译 常用快捷键 快捷键 中文名称 Ctrl R 替换文本 Ctrl 1 单行注释 单次注释 双次取消注释 Ctrl 4 块注释 单次注释 双次取消注释 F5 运行
  • Python之argparse.ArgumentParser

    一 介绍 argparse是python用于解析命令行参数和选项的标准模块 一方面可以接收运行代码时用户传入的各种参数 比如传入数据路径 另一方面也可以规范的定义程序中用到的参数 比如定义epoch及batch size等 二 使用 新建t
  • [javascript]canvas 热点区域图,框选区域并在区域显示热点

    需求 设置人员需要在图片上框选区域 并且后台针对区域读出数据后显示热点 做法 直接上代码 作者 jww dragon 163 com 依赖 jquery var goog function tag this hotpoint this sq