一、由3D场景下的坐标点,来绘制一个原生 input 输入框:
/**
* 绘制输入框
* @param {THREE.Vector3} point1
* @param {THREE.Vector3} point2
* @param {string} type 尺寸线类型【position、size】
* @param {string} axisDir 尺寸线所在轴向
* @param {string} dir 表示这一条尺寸线,在相机视角下,是左右上下的哪一条
* @returns {input} 输入框节点
*/
static makeInput(point1, point2, type, axisDir, dir) {
if (!type || !axisDir || !dir) return;
// 主要数据
const line3 = new THREE.Line3(point1, point2);
const distance = line3.distance();
const selected = StorageMgr.datas.SELECTED;
// 获取输入框需要显示的位置(如果为标识尺寸的,则位置采用【0~1】处,如果标识位置,则采用中心点)
const centerPoint = type === 'size' ? line3.at(0.3) : line3.getCenter();
selected.object3d.parent.localToWorld(centerPoint);
const { top, left } = Compute.d3PosToScreenPos(centerPoint);
// 创建节点
const input = globalThis.document && document.createElement('input');
// 基础样式如下:
const height = 1.25;
const width = Tools.setFixed(distance).toString().length / (distance.toString().includes('.') ? 1.8 : 1.5);
const isH = dir === 'left' || dir === 'right';
const isV = dir === 'top' || dir === 'bottom';
input.style.display = 'block';
input.style.position = 'fixed';
input.style.top = top + 'px';
input.style.left = left + 'px';
input.style.width = `${width}rem`;
input.style.height = `${height}rem`;
input.style.textAlign = "center";
input.style.borderRadius = "3px";
//* 样式需求如下:
// 1. 横线显示在下方
// 2. 竖线显示在右方
//todo 3. 标尺跟随视角(移动、放大、缩小)
// 4. 选中输入框时(黑框,白底,黑字)
// 5. 选中输入框时(默认全选文字,或者光标在最后)
// 6. 不允许输入非数字和小数点之外的字符
// 7. 输入错误或输入为空时,保持并重新显示原有数据
// 8. 输入框禁用滑轮
input.style.transform = `translateX(${isH ? '-50%' : '0.1rem'}) translateY(${isV ? '-50%' : '0.1rem'})`;
input.style.border = "unset";
input.style.background = "#ffffff00";
input.style.color = "#3d6cdc";
input.onclick = function (e) {
this.style.border = "1px solid black"
this.style.background = "white";
this.style.color = "black";
this.focus();
this.select();
};
input.onblur = function (e) {
this.style.border = "unset";
this.style.background = "#ffffff00";
this.style.color = "#3d6cdc";
this.blur();
};
input.type = "number";
input.className = "dimension-input";
input.onmousewheel = function (evt) {
// 禁用滑轮事件
evt = evt || window.event;
if (evt.preventDefault) {
// Firefox
evt.preventDefault();
evt.stopPropagation();
} else {
// IE
evt.cancelBubble = true;
evt.returnValue = false;
}
return false;
};
input.onkeydown = function (e) {
// 禁用上下按钮的事件
if (['ArrowUp', 'ArrowDown'].includes(e.key)) return false;
};
// 设置用户交互的数据与动作等(回调使用匿名函数,绑定this为回调的调用者)
input.customPreValue = distance; // 用于计算前后差值的自定义属性
input.value = Tools.setFixed(distance);
input.onchange = function (e) {
this.blur();
// 输入错误或输入为空时,保持并重新显示原有数据
if (['', ' '].includes(e.target.value)) return this.value = Tools.setFixed(this.customPreValue);
// 实例回调
let value = Number(e.target.value);
if (type === 'position') value = this.customPreValue - value; // 如果是位置,则传递差值
selected.onDimensionChange(type, axisDir, value);
// 以下代码虽会因重绘而无效,但以防万一,做更新
this.customPreValue = value;
};
// 返回输入框
return input;
}
二、css 如下:
/* 隐藏number输入框的上下按键 */
.dimension-input::-webkit-outer-spin-button,
.dimension-input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
.dimension-input[type="number"]{
-moz-appearance: textfield;
}
版权声明:本文为qq_46331926原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。