php+html+canvas实现树状打开的结构

html框架代码

html 复制代码
<div id="container" class="container">
	<canvas id="mycanvas" class="con_box mycanvas"></canvas>
	<div id="mydiv" class="con_box mydiv">
		<div class="roots roots_js"></div>
	</div>
</div>

js代码

javascript 复制代码
<script type="text/javascript">
	$(function () {
		layui.use([
			"layer",
		], function () {
			layer = layui.layer;

			var ecanvas = document.getElementById("mycanvas");
			var ctx = ecanvas.getContext("2d");

			// 画线
			function lines_canvas() {
				// obj
				var obj_ = $(".tree_box:nth-child(2)").children();
				if (!obj_.length) {
					return false; //根节点下没有元素时避免报错
				}
				var obj_st = obj_.first();
				var obj_e = obj_.last();
				// left
				var left_st = obj_st.position().left + (obj_st.outerWidth(true) / 2);
				var left_e = obj_e.position().left + (obj_e.outerWidth(true) / 2);
				var left_cen = $(".roots").position().left + ($(".roots").outerWidth(true) / 2);
				// top
				var top_st = $(".roots").position().top + $(".roots").outerHeight(true);
				top_st += window.innerWidth * 0.02; //避免连接线从上面漏出
				var top_e = $(".tree_box:nth-child(2)").position().top + (window.innerWidth * 0.15);
				var top_cen = top_st + ((top_e - top_st) / 2);

				// 从起始点出现的竖线
				ctx.beginPath();
				ctx.moveTo(left_cen, top_st);
				ctx.lineTo(left_cen, top_cen);
				ctx.stroke();

				// 覆盖所有子元素的横线
				ctx.beginPath();
				ctx.moveTo(left_st, top_cen);
				ctx.lineTo(left_e, top_cen);
				ctx.stroke();

				$.each(obj_, function (key, vo) {
					// 从覆盖线到每一个子元素
					var left_tmp = $(vo).position().left + ($(vo).outerWidth(true) / 2);
					ctx.beginPath();
					ctx.moveTo(left_tmp, top_cen);
					ctx.lineTo(left_tmp, top_e);
					ctx.stroke();
				});

				$.each($(".start"), function (key, vo) {
					// console.log(vo);

					// obj
					var obj_p = $(vo).parent();
					var obj_np = obj_p.next();
					var obj_cnp = obj_np.children();
					var obj_st = obj_cnp.first();
					var obj_e = obj_cnp.last();
					// left
					var s_x = $(vo).position().left + ($(vo).outerWidth(true) / 2);
					var first_x = obj_st.position().left + (obj_st.outerWidth(true) / 2);
					var st_x = Math.min(s_x, first_x);
					var last_x = obj_e.position().left + (obj_e.outerWidth(true) / 2);
					var e_x = Math.max(s_x, last_x);
					// top
					var st_y = obj_p.position().top + obj_p.outerHeight(true);
					st_y += window.innerWidth * 0.01; //避免连接线从上面漏出
					st_y += -$(obj_p).height() + $(vo).outerHeight(true); //兼容高度不同的元素
					var e_y = $(obj_np).position().top + (window.innerWidth * 0.15);
					var cen_y = st_y + ((e_y - st_y) / 2);

					// 从起始点出现的竖线
					ctx.beginPath();
					ctx.moveTo(s_x, st_y);
					ctx.lineTo(s_x, cen_y);
					ctx.stroke();

					// 覆盖所有子元素的横线
					ctx.beginPath();
					ctx.moveTo(st_x, cen_y);
					ctx.lineTo(e_x, cen_y);
					ctx.stroke();

					$.each(obj_cnp, function (key, vo) {
						// 从覆盖线到每一个子元素
						var left_tmp = $(vo).position().left + ($(vo).outerWidth(true) / 2);
						ctx.beginPath();
						ctx.moveTo(left_tmp, cen_y);
						ctx.lineTo(left_tmp, e_y);
						ctx.stroke();
					});
				});

				// ecanvas.toDataURL();
			}

			// canvas
			function init_canvas() {
				console.log("init_canvas");

				$("#mycanvas").attr("width", $("#mydiv").outerWidth(true));
				$("#mycanvas").attr("height", $("#mydiv").outerHeight(true));

				ctx.save();
				ctx.restore();

				ctx.strokeStyle = "#a4aabb"; //线颜色

				lines_canvas();
			}

			// 数据添加到页面
			function show_datas(infos, len) {
				// console.log(infos);
				if (infos.depth == 0) {
					$(".roots_js").html(infos.names);
					$(".roots_js").attr("iid", infos.id);
				} else {
					var obj = $(".tree_box:nth-child(" + (infos.depth + 1) + ")");
					if (!obj.length) {
						$(".mydiv").append(`<div class="tree_box"></div>`);
					}
					var obj = $(".tree_box:nth-child(" + (infos.depth + 1) + ")");
					var clas = "";
					if (infos.children) {
						clas += "start";
					}
					if (infos.last == 1) {
						clas += "lebg" + infos.linkeva;
					}
					// var ids = "node" + infos.id;
					var ids = infos.id;
					if (!$("#" + ids).length) {
						obj.append(`<div id="` + ids +
							`" class="tree_cons end ` + clas +
							`" depth="` + infos.depth +
							`" onclick="get_tree(` + infos.id + `,this)">` +
							infos.names +
							`</div>`);
					}
				}
				if (infos.children) {
					$.each(infos.children, function (key, vo) {
						show_datas(vo);
					});
				}
				lens++;
				if (len == lens) {
					init_canvas();
				}
			}

			// 获取数据并调整页面内容
			window.get_tree = function (iid, that) {
				// console.log($(".roots").attr("iid"));
				// console.log(iid);
				// console.log(that);

				if (iid == $(".roots").attr("iid")) {
					console.log("重复");
					return false;
				}

				if (typeof (ctx) != "undefined") {
					ctx.save();
					ctx.restore();
				}

				if (typeof (that) != "undefined") {
					// console.log(1);
					var obj = $(that).parent();
					obj.nextAll().remove();
					$.each(obj.children(), function (key, vo) {
						$(vo).removeClass("start");
					});
				} else {
					// console.log(2);
					$(".tree_box").remove();
				}

				$.post("/index/pro/gets_tree", {
					iid: iid,
				}, function (params) {
					// console.log(params);
					// console.log(params.nodes);
					// console.log(params.len);
					lens = 0;
					if (params.nodes.children) {
						$(that).addClass("start");
					}
					show_datas(params.nodes, params.len);
				}, "json");
			}

			get_tree(`{$iid}`);

			// $(".block2_item").click(function () {
			// 	get_tree($(this).attr("iid"));
			// });

		});
	});

</script>

php代码

php 复制代码
public $t_pri = "ids"; //主键
public $t_ck = "children"; //子数据集key
public $t_fields; //查询字段

function _initialize()
{
	parent::_initialize();

	$this->t_fields = [
		"id",
		"id" => $this->t_pri,
		"names",
		// "names" => "name", //echarts
		// "names" => "id", //antv g6
		// "names" => "label", //antv g6
		"last",
		"depth",
		"nuop",
		"linkeva",
	];
}

// 树图数据获取
function gets_tree()
{
	$iid = intval(input("iid"));
	if (!$iid) {
		$iid = Db::name("industrial")->where("iid", "")->value("id");
	}
	$this->assign("iid", $iid);

	$t_arr = $this->datas_tree(0, $iid);
	$infos = [];
	if (!empty($t_arr[0])) {
		$infos = &$t_arr[0];
	}

	if (isset($infos[$this->t_pri])) {
		if ($infos["depth"] == 0) {
			$infos["x"] = 1000;
		}
	}

	$res = [];
	// $res["infos"] = $infos;
	$res["nodes"] = $infos;
	$res["len"] = count($this->to_onelv($t_arr));
	// $res["lists"] = $t_arr;
	// $res["nodes"] = $this->to_pos($infos);
	// $res["edges"] = $this->to_edges($infos);
	// $res["nodes"] = $this->to_onelv($t_arr);
	$res["code"] = 0;
	return $res;
}

// 树图数据获取
function datas_tree($iid = 0, $id = 0)
{
	$whe["iid"] = $iid;
	$whe["id"] = $id;
	$whe = array_filter($whe);
	$lists = Db::name("industrial")->field($this->t_fields)->where($whe)->select();

	if (!empty($lists[0][$this->t_pri])) {
		$t_arr = $this->datas_tree($lists[0][$this->t_pri]);
		if (!empty($t_arr)) {
			$lists[0][$this->t_ck] = $t_arr;
		}
	}

	return $lists;
}

// 树状结构数据展开
function to_onelv($lists)
{
	$len = 0;
	foreach ($lists as $key => &$value) {
		if (!empty($value["children"])) {
			$len++;
			$lists = array_merge($lists, $value["children"]);
			unset($value["children"]);
		}
	}
	if ($len) {
		$lists = $this->to_onelv($lists);
	}
	return $lists;
}

代码效果

相关推荐
电商API_180079052472 小时前
反向海淘多币种结算:汇率该在哪个时点被冻结
java·开发语言·数据库·跨境电商·1688 api
开开心心就好3 小时前
多份文档怎么批量打印?支持多种格式
java·服务器·开发语言·pdf·ocr·散列表·启发式算法
Want5953 小时前
Python炫酷烟花①
开发语言·python
猫头_3 小时前
AI 流式传输工程指南:模型吐出半截 JSON,前端如何边收边渲染?
前端·javascript·llm
星夜离尘564 小时前
MATLAB 线性去趋势为何损伤低频正弦:联合最小二乘估计幅相与漂移
开发语言·matlab
我的div丢了肿么办4 小时前
element-plus表格使用:filter-method过滤导致出现无数据
前端·javascript·vue.js
程序员清风4 小时前
双指针与滑动窗口:高效处理连续区间
开发语言·算法
YIAN5 小时前
入门篇:LangGraph 入门实战:从零搭建最简单的 RAG 问答系统
javascript·langchain
YIAN5 小时前
高级篇:进阶多跳 RAG:子问题拆解 + 循环检索 + 智能规划,搞定复杂链式问题
javascript·langchain