AngularJS中实现上下字段点击配对并绘制连接线的方法
AngularJS实现上下字段点击配对连线
实现思路
用AngularJS管理选中状态与配对数据,借助SVG绘制连线(SVG支持坐标计算与动态元素添加,适合这类连线场景)。核心逻辑是:
- 记录首次点击的字段(上方/下方)
- 第二次点击对应区域字段时,建立配对关系
- 基于两个字段的DOM坐标计算连线起点与终点,动态生成SVG连线
完整代码实现
HTML结构
<div ng-app="matchApp" ng-controller="MatchController"> <!-- 上方字段容器 --> <div class="field-container top-container"> <div class="field" ng-repeat="field in topFields" ng-click="selectField(field, 'top')" ng-class="{'selected': field.selected, 'matched': field.matched}"> {{field.name}} </div> </div> <!-- SVG连线容器,绝对定位覆盖在上下容器之间 --> <svg class="connection-svg"></svg> <!-- 下方字段容器 --> <div class="field-container bottom-container"> <div class="field" ng-repeat="field in bottomFields" ng-click="selectField(field, 'bottom')" ng-class="{'selected': field.selected, 'matched': field.matched}"> {{field.name}} </div> </div> </div>
AngularJS控制器
angular.module('matchApp', []) .controller('MatchController', ['$scope', '$document', function($scope, $document) { // 初始化上下各10个字段 $scope.topFields = Array.from({length:10}, (_,i) => ({id: `top-${i+1}`, name: `上方字段${i+1}`, selected: false, matched: false})); $scope.bottomFields = Array.from({length:10}, (_,i) => ({id: `bottom-${i+1}`, name: `下方字段${i+1}`, selected: false, matched: false})); let selectedField = null; // 存储首次选中的字段 const svg = $document[0].querySelector('.connection-svg'); // 字段点击事件 $scope.selectField = function(field, type) { // 如果字段已配对,不响应点击 if (field.matched) return; // 首次选中字段 if (!selectedField) { field.selected = true; selectedField = {field, type}; return; } // 点击同区域字段,切换选中状态 if (selectedField.type === type) { selectedField.field.selected = false; field.selected = true; selectedField = {field, type}; return; } // 跨区域选中,建立配对 selectedField.field.selected = false; field.selected = false; selectedField.field.matched = true; field.matched = true; // 计算连线坐标并绘制 drawConnection(selectedField.field.id, field.id); selectedField = null; }; // 绘制连线函数 function drawConnection(topId, bottomId) { const topEl = $document[0].querySelector(`[ng-repeat="field in topFields"]:nth-child(${topId.split('-')[1]})`); const bottomEl = $document[0].querySelector(`[ng-repeat="field in bottomFields"]:nth-child(${bottomId.split('-')[1]})`); // 获取元素中心坐标 const topRect = topEl.getBoundingClientRect(); const bottomRect = bottomEl.getBoundingClientRect(); const svgRect = svg.getBoundingClientRect(); const startX = topRect.left + topRect.width/2 - svgRect.left; const startY = topRect.bottom - svgRect.top; const endX = bottomRect.left + bottomRect.width/2 - svgRect.left; const endY = bottomRect.top - svgRect.top; // 创建SVG连线元素 const line = $document[0].createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', startX); line.setAttribute('y1', startY); line.setAttribute('x2', endX); line.setAttribute('y2', endY); line.setAttribute('stroke', '#2196F3'); line.setAttribute('stroke-width', 2); svg.appendChild(line); } }]);
CSS样式
.field-container { display: flex; justify-content: space-around; padding: 20px; margin-bottom: 80px; /* 给SVG留空间 */ } .bottom-container { margin-top: 80px; } .field { padding: 10px 20px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; transition: all 0.3s; } .field.selected { background-color: #E3F2FD; border-color: #2196F3; } .field.matched { background-color: #C8E6C9; border-color: #4CAF50; cursor: not-allowed; } .connection-svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; /* 不影响字段点击 */ z-index: -1; }
关键说明
- 状态管理:通过
selected(选中状态)和matched(已配对状态)两个属性控制字段样式与交互逻辑,避免重复配对。 - 坐标计算:利用
getBoundingClientRect()获取字段的位置信息,转换为SVG内部坐标后绘制连线。 - SVG层级:设置
pointer-events: none确保SVG不会干扰字段的点击事件,同时用绝对定位覆盖整个页面,保证连线能在任意位置绘制。
内容的提问来源于stack exchange,提问作者Saravanan Thangadurai
相关产品推荐
相关产品推荐

