用3Dmol.js展示3D分子结构

几个月前有个3D开发需求,展示化学分子结构,需求学科专业化程度较高了!

对方推荐了JSmol,但是这个跟Jmol(Java分子结构库)紧密结合。最终查找资料找到纯前端解决方案,决定用3Dmol.js实现。本文简单记录一下使用方法,免得忘了!

1.什么是3Dmol.js

官方介绍:

3Dmol.js是一个基于WebGL的面向对象的JavaScript库,用于在线分子可视化 - 无需Java! 使用3Dmol.js,您可以将精美渲染的分子可视化添加到您的Web应用程序中。

特性

  • 支持pdb、sdf、mol2、xyz和cube格式
  • 并行分子表面计算
  • 球体、棒状、线条、十字、卡通和曲面样式
  • 基于原子属性的选择和样式设置
  • 标签
  • 与分子数据的可点击交互
  • 几何形状,包括球体和箭头

Github地址 https://github.com/3dmol/3Dmol.js

官网地址 https://3dmol.org/

2.使用3Dmol.js

2.1 安装

sh 复制代码
pnpm add 3dmol

该库基于typescript开发,友好支持typescript!

或者使用script形式引入

html 复制代码
<script src="https://3Dmol.org/build/3Dmol-min.js"></script>

<script src="https://3Dmol.org/build/3Dmol.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/3Dmol/2.0.1/3Dmol-min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/3Dmol/2.0.1/3Dmol.js"></script>

2.1 创建场景

html 复制代码
<div id="container" style="height:800px;width:800px"></div>
ts 复制代码
import * as $3Dmol from '3dmol/build/3Dmol.js';

const container=document.getElementById("container");
//创建视图
const viewer = createViewer(container, {backgroundColor: "orange"});

//添加绿色球体
    viewer.addSphere({center: {x: 0, y: 0, z: 0}, radius: 10.0, color: "green"});
    //缩放适配渲染内容大小
    viewer.zoomTo();
    //开始渲染
    viewer.render();
    //缩放动画  2秒缩放到0.8视角
    viewer.zoom(0.8, 2000);

效果如下

常用的几个配置

  • backgroundColor背景颜色

  • antialias是否开启抗锯齿

  • backgroundAlpha背景透明度,范围[0~1]

  • defaultcolors3D分子模型默认颜色,如使用JS模型颜色$3Dmol.elementColors.Jmol,那么3D分子模型的样式会是:

    • H氢原子白色
    • O氧原子红色
    • 元素原子都会配置对应颜色作为区别标识
  • 更多视图方法的配置可以看官网文档createViewer

常用的的方法

  • render()渲染场景内容
  • resize()调整大小,用于窗口大小变化的时候
  • spin(axis, speed)视角变化,自动绕某个轴旋转
  • clear()清空场景所有
  • setConfig(c)修改更新配置
  • setStyle()修改模型全局样式配置
  • 更多视图对象操作请看GLViewer`

2.2 添加形状并设置样式

添加球体

ts 复制代码
const sphere= viewer.addSphere({center: {x: 2.0, y: 0, z: 0}, radius: 1.0, color: "red"});

const sphere1=viewer.addSphere({center: {x: -2.0, y: 0, z: 0}, radius: 1.0, color: "red", opacity: 0.5});

参数说明

  • center中心坐标
  • radius半径大小
  • color颜色
  • opacity透明度 可以使用updateStyle修改样式
ts 复制代码
 sphere.updateStyle({color: "blue"});
 viewer.render();

注意:形状修改样式后记得执行 viewer.render才能重新渲染生效。

添加柱体

ts 复制代码
viewer.addCylinder({
      start: {x: 1.0, y: 0.0, z: 0.0},
      end: {x: -1.0, y: 0.0, z: 0.0},
      radius: 0.2,
      color: "white",
      fromCap: $3Dmol.CAP.FLAT, 
      toCap: $3Dmol.CAP.ROUND  
    });

参数说明

  • start 开始坐标

  • end结束坐标

  • radius半径大小

  • color颜色

  • fromCap开始端是否圆角,NONE无,FLAT平角, ROUND圆角

  • toCap结束端是否圆角,NONE无,FLAT平角, ROUND圆角

可以开启虚线,多段柱体

ts 复制代码
viewer.addCylinder({
      start: {x: 1.0, y: 0.0, z: 0.0},
      end: {x: -1.0, y: 0.0, z: 0.0},
      radius: 0.2,
      color: "white",
      fromCap: $3Dmol.CAP.FLAT,
      toCap: $3Dmol.CAP.FLAT,
      dashed: true,
      dashLength: 0.5,
      gapLength: 0.3
    });

参数说明

  • dashed是否开启虚线
  • dashLength段长度
  • gapLength间隔长度

添加标签

ts 复制代码
  viewer.addLabel("分子结构", {
      alignment: "center",
      font: "sans-serif",
      fontSize: 18,
      fontColor: "white",
      fontOpacity: 1,
      borderThickness: 2.0,
      borderColor: "blue",
      borderOpacity: 1,
      backgroundColor: "black",
      backgroundOpacity: 0.5,
      position: {x: 0.0, y: 0.0, z: 0.0},
      inFront: true,
      showBackground: true
    });

参数说明

  • alignment对齐方式,可选项"topLeft", "topCenter", "topRight", "centerLeft", "center", "centerRight", "bottomLeft", "bottomCenter", "bottomRight",也可以是xy坐标
  • position标签位置
  • inFront是否总在模型前面
  • font字体系列
  • fontSize字体大小
  • fontColor字体颜色
  • fontOpacity字体透明度
  • borderThickness边框宽度
  • borderColor边框颜色
  • borderOpacity边框透明度
  • showBackground是否显示背景
  • backgroundColor背景颜色
  • backgroundOpacity背景透明度

还有其他更多形状,如矩形,箭头,曲线等,可以到官方API文档查看怎么使用

https://3dmol.org/doc/GLViewer.html

2.3 加载3D分子结构模型

通过请求下载获取并添加D分子结构模型,推荐这种方法,3Dmol.js自动识别格式并解析加载,很方便。

ts 复制代码
download("/1ycr.pdb", viewer, {multimodel: true, frames: true}, (model: any) => {
      console.log("🚀 ~ model:", model);
      viewer.setStyle({}, {cartoon: {color: "spectrum"}});
      viewer.render();
    });

viewer.setStyle设置全局样式,使得3D分子结构结构以卡通模式显示

通过打印的模型信息,可以看到模型相关数据有A,B,C三条链,α,β,γ三个角的角度

设置分子结构链A为绿色

ts 复制代码
viewer.setStyle({chain: "A"}, {cartoon: {color: "green"}});

通过模型格式文本解析添加3D分子结构

这是7个水分子的3D分子结构模型文本

txt 复制代码
symmetry c1
 OpenBabel01312416543D

 21 14  0  0  0  0  0  0  0  0999 V2000
   0.59904   0.98737  -3.22086 O   0  0  0  0  0  0
  -1.51456   1.20243  -0.31112 O   0  0  0  0  0  0
   0.13045   0.14890  -1.25059 O   0  0  0  0  0  0
  -0.20952  -0.68359  -1.35644 H   0  0  0  0  0  0
  -0.69349   0.71484  -0.83445 H   0  0  0  0  0  0
  -0.05336   1.70041  -3.27063 H   0  0  0  0  0  0
   0.39550   0.49975  -2.28181 H   0  0  0  0  0  0
  -2.04082   1.71243  -0.64258 H   0  0  0  0  0  0
  -1.91286   0.87596   0.33191 H   0  0  0  0  0  0
  -0.80317  -1.78194  -1.51703 O   0  0  0  0  0  0
   4.83471   2.50607   2.80654 H   0  0  0  0  0  0
  -2.85573  -2.53790  -1.84489 O   0  0  0  0  0  0
  -0.13238  -2.61116  -1.78592 H   0  0  0  0  0  0
  -1.84863  -2.10567  -1.62688 H   0  0  0  0  0  0
  -3.18539  -2.82307  -2.85475 H   0  0  0  0  0  0
  -3.60191  -2.86990  -1.10817 H   0  0  0  0  0  0
   5.44460   1.75213   2.56206 O   0  0  0  0  0  0
   6.37580   2.09935   2.45041 H   0  0  0  0  0  0
   1.49202  -0.02736   0.38172 O   0  0  0  0  0  0
   1.15940   0.32819   1.19843 H   0  0  0  0  0  0
   0.74030   0.06841  -0.41057 H   0  0  0  0  0  0
  4  3  1  0  0  0
  5  3  1  0  0  0
  6  1  1  0  0  0
  7  1  1  0  0  0
  8  2  1  0  0  0
  9  2  1  0  0  0
 13 10  1  0  0  0
 14 10  1  0  0  0
 15 12  1  0  0  0
 16 12  1  0  0  0
 17 11  1  0  0  0
 18 17  1  0  0  0
 20 19  1  0  0  0
 21 19  1  0  0  0
M  END
$$$$

设置采用Jmol模型颜色风格,

ts 复制代码
const  viewer = $3Dmol.createViewer(container, {
      defaultcolors: $3Dmol.elementColors.Jmol,
     backgroundColor: "#f9f9f9"      
    });
    
  viewer.addModel(data, 'sdf');
  viewer.setStyle(
        {},
        {
          stick: {
            radius: 0.15
          },
          sphere: {
            scale: 0.25
          }
        }
      );
  • 其中模型格式可选'pdb', 'sdf', 'xyz', 'pqr', 'mol2',这个需要人工识别格式。非专业人士真不懂,所以还是推荐download
  • viewer.setStyle通过设置全局样式为棍棒模式

2.4 切换3D分子结构模式

有四种常见的分子展示模式

ts 复制代码
  const modes = [
    {label: "球棍模式", value: "ball"},
    {label: "线框模式", value: "line"},
    {label: "球体模式", value: "spacefill"},
    {label: "卡通模式", value: "cartoon"}
  ];

线框模式的全局样式设置

ts 复制代码
viewer.setStyle(
            {},
            {
              stick: {
                radius: 0.15
              }
            }
          );

球体模式的全局样式设置

ts 复制代码
viewer.setStyle(
            {},
            {
              sphere: {
                scale: 0.75
              }
            }
          );

球棍模式的全局样式设置

ts 复制代码
viewer.setStyle(
            {},
            {
              stick: {
                radius: 0.15
              },
              sphere: {
                scale: 0.25
              }
            }
          );

卡通模式的全局样式设置,注意卡通模式是特殊的,仅适用于蛋白质或核酸二级结构的可视化,如果是水分子之类则可能空白一片或者只有线。

ts 复制代码
viewer.setStyle({}, {cartoon: {color: "spectrum"}});

让视图内容自动沿y轴旋转

ts 复制代码
viewer.spin("y");

给模型原子添加标签

打印模型信息,可以看到在atoms属性有所有原子的信息

  • elem该原子的元素
  • xyz对应原子的位置

遍历所有原子即可添加所有元素标签

ts 复制代码
 model.atoms.forEach((item: any) => {
            viewer.addLabel(item.elem, {
            alignment:'center',
              position: {
                x: item.x,
                y: item.y,
                z: item.z
              },
              fontColor: "black",
              showBackground: false
            });
          });

当然可以通过移除所有标签和模型,添加新的模型和标签

ts 复制代码
  viewer.removeAllLabels();
 viewer.removeAllModels();

销毁视图前记得清空

ts 复制代码
viewer.clear();

3. 总结

  1. 3Dmol.js的缩放操作跟Three.js的OrbitControls有些不同,滚轮缩放是相反的,向前滚是缩小,向后滚是放大,并且找了一圈配置项,没有发现修改缩放操作的配置。
  2. 3Dmol.js的不能移动操作,只能旋转。不过可以通过viewer.setView()来配置视角位置。
  • 3D分子结构不论大小都可以通过viewer.zoomTo()来适配视角,可以通过viewer.zoom(zoom,time)设置缩放等级
  1. 3Dmol.js限制缩放视角大小的参数很鸡肋,是根据相机距离物体的距离根据判断条件,而不同分子结构的大小是不限的,有的总大小可能是100+,有的总大小只有0.5,那么限制值就得谨慎计算出合适值才行,否则就会导致显示的分子结构太小或太大。
ts 复制代码
const viewer= $3Dmol.createViewer(container,{
  lowerZoomLimit: 100,
    upperZoomLimit: 200,
 })
 
viewer.setZoomLimits(lower,upper);

通过viewer.getView()返回视角参数[pos.x, pos.y, pos.z, rotationGroup.position.z, q.x, q.y, q.z, q.w ],对应平移、缩放和旋转四元数,可以获取当前视角距离z

然后我以为通过zoom(minZoom)和zoom(maxZoom)获取对应缩放值的视角距离限制,结果发现lowerZoomLimitupperZoomLimit跟getView的z距离不是一个东西。

ts 复制代码
 viewer.zoom(0.5);
    const v = viewer.getView();
    const minZ = v[3];
    viewer.zoom(3);
    const v1 = viewer.getView();
    const maxZ = v1[3];
    viewer.setZoomLimits(minZ, maxZ);
    viewer.render();

于是,查看了一下文档,有个setViewChangeCallback方法可以监听视角变化

ts 复制代码
viewer.setViewChangeCallback((view: number[]) => {
        console.log("🚀 ~ view:", view[3]);
      });

可以看到,物体离摄像机越远,即缩小,则view的z距离值越小,物体离摄像机越近,即放大,则view的z距离值越大。

3.1 实现通用视角范围限制

第一步,遍历模型元素,获取其包围框范围

ts 复制代码
const box = {
        minx: Number.MAX_SAFE_INTEGER,
        miny: Number.MAX_SAFE_INTEGER,
        minz: Number.MAX_SAFE_INTEGER,
        maxx: Number.MIN_SAFE_INTEGER,
        maxy: Number.MIN_SAFE_INTEGER,
        maxz: Number.MIN_SAFE_INTEGER
      };
      model.atoms.forEach((item: any) => {
        box.minx = Math.min(box.minx, item.x);
        box.maxx = Math.max(box.maxx, item.x);

        box.miny = Math.min(box.miny, item.y);
        box.maxy = Math.max(box.maxy, item.y);

        box.minz = Math.min(box.minz, item.z);
        box.maxz = Math.max(box.maxz, item.z);
      });

      const size = {
        x: box.maxx - box.minx,
        y: box.maxy - box.miny,
        z: box.maxz - box.minz
      };
const distance = Math.max(size.x, size.y, size.z);

第二步,zoomTo()后获取最佳适配视角作为基础距离

ts 复制代码
const v = viewer.getView();
 const baseDistance = v[3];

第三步,根据包围框大小,设置缩放最大最小距离z,监听视角变化,基于基础距离baseDistance进行对比和限制

ts 复制代码
const minDistance = -distance * 3;
      const maxDistance = distance * 2;
      viewer.setViewChangeCallback((view: number[]) => {
        const z = view[3];
        if (z < baseDistance + minDistance) {
          //缩小限制
          view[3] = baseDistance + minDistance;
          viewer.setView(view);
        } else if (z > baseDistance + maxDistance) {
          //放大限制
          view[3] = baseDistance + maxDistance;
          viewer.setView(view);
        }
      });

4. Github地址

https://github.com/xiaolidan00/3dmol-project

参考

相关推荐
还是大剑师兰特3 小时前
TypeScript 面试题及详细答案 100题 (11-20)-- 基础类型与类型操作
typescript·大剑师·typescript教程·typescript面试题
用户47949283569154 小时前
TypeScript 和 JavaScript 的 'use strict' 有啥不同
前端·javascript·typescript
我要学习别拦我~6 小时前
数据比例与尺度:如何避免让图“放大”差异
经验分享·信息可视化·数据可视化
用户47949283569159 小时前
还不知道'use strict'的作用?这篇文章给你讲清楚
前端·javascript·typescript
supermapsupport1 天前
SuperMap iClient3D for WebGL 调用GPA服务实现地质体模型裁剪封边
3d·webgl
乐影1 天前
TS 模板字符串类型:从基础到进阶的类型编程魔法
前端·typescript
SelectDB1 天前
岚图汽车 x Apache Doris : 海量车联网数据实时分析实践
数据分析·github·数据可视化
柯南二号1 天前
【大前端】 TypeScript vs JavaScript:全面对比与实践指南
前端·javascript·typescript
杨超越luckly1 天前
HTML应用指南:利用GET请求获取全国中国建设银行网点位置信息
前端·arcgis·html·数据可视化·门店数据