three.js 航拍全景图(+陀螺仪)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

右上角陀螺仪也可点击,需要https的环境,手动下载DeviceOrientationControls.js文件
后台包含打点功能

<template><div id="quanjing" style="width: 100vw; height: 100vh; overflow: hidden"><spanid="tip"style="position: absolute; color: red; top: 0; left: 0"></span><!-- 封面图切换结束 --><img:src="imageUrl + aerialData.asteroidImg"style="position: absolute;top: 50%;height: 100vh;left: 50%;transform: translate(-50%, -50%);"v-if="isShowFM"/><imgv-if="isShowHand"src="../assets/images/quanjingHand.png"style="z-index: 2;position: absolute;top: 50%;left: 50%;margin-left: -80px;margin-top: -90px;border-radius: 10px;width: 160px;height: 180px;"/><!-- 封面图切换结束 --><imgsrc="../assets/images/quanjinglogo.png"style="position: absolute; top: 10px; left: 10px; width: 100px"/><!-- 右上角全景图标切换开始 --><div class="rig-list" id="hangpaiIcon" v-if="!isPc"><img v-if="isFull" src="../assets/images/hangpaiIcon.png" /><img v-else src="../assets/images/hangpaiIcon2.png" /></div><!-- 右上角全景图标切换开始结束 --><!-- 标注增删改查开始 --><div class="addPoint" v-if="isShowPoint"><div class="row" v-for="(item, index) in biaojiList" :key="index"><div>地名:</div><input placeholder="" class="inp" v-model="item.title" /><div>x:</div><input placeholder="" class="inp" v-model="item.x" /><div>y:</div><input placeholder="" class="inp" v-model="item.y" /><div>z:</div><input placeholder="" class="inp" v-model="item.z" /><divstyle="background-color: rgb(253, 143, 143);margin-left: 10px;padding: 0 5px;"@click="delPoint(index)">删除</div><divstyle="background-color: rgb(249, 253, 143);margin-left: 10px;padding: 0 5px;"@click="huoqu(index)">获取</div><divstyle="background-color: rgb(104, 255, 111);margin-left: 10px;padding: 0 5px;"@click="baocun(index)">保存</div></div><div class="row"><div@click="addPoint"style="background-color: rgb(143, 200, 253);margin-left: 10px;padding: 0 5px;">新增</div><div@click="submitPoint"style="background-color: rgb(143, 253, 191);margin-left: 10px;padding: 0 5px;">上传</div><span style="font-size: 12px">(*1.点击获取按钮鼠标右键点击在航拍图上。2.确认坐标后点击同行保存按钮。3.标注完成后点击上传按钮)</span></div></div><!-- 标注增删改查结束 --><div id="biaozhudian"></div></div>
</template>
<!-- [{"title":"东直门","x":"445.42720890862677","y":"-218.0846523283593","z":"-57.86235074865762"}] -->
<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { DeviceOrientationControls } from "three/examples/jsm/controls/DeviceOrientationControls.js";
import TWEEN from "@tweenjs/tween.js";
import {CSS2DRenderer,CSS2DObject,
} from "three/examples/jsm/renderers/CSS2DRenderer.js";
import { aerialData, editAerial, updateMark } from "@/api/house/house.js";
export default {name: "quanjing",data() {return {isShowFM: false,isShowHand: false,imageUrl: "https://obj.qiniu.fangdadi.com/",title: "房大地-全景",scene: null,camera: null,renderer: null,css2Renderer: null,loader: null,texture: null,sphereGeometry: null,mesh: null,axesHelper: null,tween: null,controls: null,controls2: null,//标点vector: null,screenVector: null,doc: null,activePoint: null,div: null,raycaster: null,mouse: null,tagObject: null,//陀螺仪clock: null,isFull: null,dcontrols: null,aerialData: {},biaojiList: [],pointList: [{ title: "", x: "", y: "", z: "" }],pointIndex: null,isShowPoint: false,tagArray: [],isPc: false,};},watch: {},mounted() {//判断是什么环境,手机还是电脑if (document.documentElement.clientWidth < 720) {this.isPc = false;} else {this.isPc = true;}this.getAerialData();// 点击右上角陀螺仪document.getElementById("hangpaiIcon").addEventListener("click", this.fullOrExit, false);},methods: {//获取数据getAerialData() {let idNum = null;console.log(this.$route.params.id.split("+"));if (this.$route.params.id.split("+").length == 2) {console.log(1111);this.isShowPoint = true;idNum = this.$route.params.id.split("+")[0];} else {console.log(22222222);this.isShowPoint = false;idNum = this.$route.params.id;}aerialData(idNum).then((res) => {if (res.data) {this.aerialData = res.data;this.isShowFM = true;if (res.data.markData) {console.log(JSON.parse(res.data.markData));this.biaojiList = JSON.parse(res.data.markData);console.log(" this.biaojiList ", this.biaojiList.length);}}}).then(() => {this.clock = new THREE.Clock();this.container = document.body;this.isFull = false;//判断是什么环境,手机还是电脑// if (document.documentElement.clientWidth < 720) {// }this.initThree();this.clickBiaoji();this.objTween();window.addEventListener("pointerdown", this.onMouseDown, false);var _this = this;// 更改渲染器画布大小window.onresize = function () {// 重置渲染器输出画布canvas尺寸_this.renderer.setSize(window.innerWidth, window.innerHeight);// 全屏情况下:设置观察范围长宽比aspect为窗口宽高比_this.camera.aspect = window.innerWidth / window.innerHeight;// 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix// 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)// 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵_this.camera.updateProjectionMatrix();};});},// /**// 准备全景图像:// 首先,您需要获得或创建全景图像。全景图像是一种呈现完整360度视野的特殊图像。您可以使用专业相机拍摄全景照片,或者从互联网上获取全景图像。确保全景图像采用通常的全景图像格式,如equirectangular格式。// 设置Three.js场景:// 创建一个HTML页面,引入Three.js库。您可以从Three.js官方网站下载或使用CDN来加载库。// 创建Three.js场景:// 在您的HTML页面中,创建一个Three.js场景、相机和渲染器。//  */// // 相机第一个参数fov 130->75initThree() {this.scene = new THREE.Scene();this.camera = new THREE.PerspectiveCamera(130,window.innerWidth / window.innerHeight,1,1000);this.renderer = new THREE.WebGLRenderer();this.renderer.setSize(window.innerWidth, window.innerHeight);this.renderer.domElement.style.zIndex = -1;this.css2Renderer = new CSS2DRenderer();this.css2Renderer.setSize(window.innerWidth, window.innerHeight);this.LoadingImg();},LoadingImg() {var _this = this;/*加载全景图像:使用Three.js的TextureLoader加载全景图像。*/this.loader = new THREE.TextureLoader();this.texture = this.loader.load(this.imageUrl + this.aerialData.panoramaImg,function (obj) {console.log("加载完了");_this.isShowFM = false;}); // If texture is used for color information, set colorspace.this.texture.encoding = THREE.sRGBEncoding;this.sphereGeometry = new THREE.SphereGeometry(500, 60, 40);this.sphereGeometry.scale(-1, 1, 1); //创建的球形几何体执行这个方法, 镜像就正回来了this.sphereMaterial = new THREE.MeshBasicMaterial({map: this.texture,side: THREE.DoubleSide,});this.mesh = new THREE.Mesh(this.sphereGeometry, this.sphereMaterial);this.scene.add(this.mesh);// // AxesHelper:辅助观察的坐标系// this.axesHelper = new THREE.AxesHelper(150);// this.scene.add(this.axesHelper);/*设置相机视角:将相机朝向全景图像的中心,以确保全景图像填充整个视野。*/this.camera.position.set(0, 500, 0.1);this.camera.lookAt(0, 0, 0);/*** 模型旋转*/this.mesh.rotateY(-Math.PI / 2); //绕x轴旋转π/2},objTween() {var _this = this;// // 旋转过渡效果this.tween = new TWEEN.Tween({x: 0,y: 500,z: 10,ry: -Math.PI / 2,fov: 130,}) // 开始位置(2D).to({x: 0,y: 0,z: 0.1,ry: 0,fov: 75,},3000) // 结束位置(3D).easing(TWEEN.Easing.Quadratic.InOut) // 缓动函数.onUpdate(function (obj) {_this.camera.position.set(obj.x * 1, obj.y * 1, obj.z * 1);_this.camera.lookAt(_this.scene.position);_this.mesh.rotation.y = obj.ry;_this.camera.rotation.x = obj.ry;_this.camera.fov = obj.fov;_this.camera.updateProjectionMatrix();}).delay(3000).start().onComplete(function () {//运动结束后地图打点_this.biaoji();_this.isShowHand = true;setTimeout(() => {_this.isShowHand = false;}, 2000);});this.OrbitControlsFun();},// /*// 渲染场景:// 使用requestAnimationFrame函数循环渲染Three.js场景。// */animate() {requestAnimationFrame(this.animate);TWEEN.update(); // 更新Tween.js动画this.renderer.render(this.scene, this.camera);this.css2Renderer.render(this.scene, this.camera);if (this.isFull) {this.dcontrols.update(this.clock.getDelta());}},/*添加交互性(可选):您还可以添加鼠标或触摸交互,以允许用户在全景图像中浏览。Three.js提供了相关的控制器,如OrbitControls。*/OrbitControlsFun() {this.controls = new OrbitControls(this.camera, this.renderer.domElement);this.controls.enableZoom = false;this.controls.enablePan = false;this.controls2 = new OrbitControls(this.camera,this.css2Renderer.domElement);this.controls2.enableZoom = false;this.controls2.enablePan = false;// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景document.getElementById("quanjing").appendChild(this.renderer.domElement);this.animate();},//设置标记点开始biaoji() {if (this.tagArray.length != 0) {this.tagArray.forEach((i) => {this.scene.remove(i);});}this.biaojiList.forEach((item) => {let info1 = document.createElement("div");info1.setAttribute("class", "info1");info1.style.width = "40px";info1.style.height = "40px";info1.style.position = "absolute";info1.style.pointerEvents = "auto";var img = document.createElement("img");img.src ="https://wimg.588ku.com/gif620/20/06/28/a47c5cb0333780628b29c0e94f3b9423.gif";img.style.width = "40px";img.style.height = "40px";info1.appendChild(img);var p = document.createElement("p");p.innerText = item.title;p.style.position = "absolute";p.style.bottom = "100%";p.style.left = "-70%";p.style.minWidth = "100px";p.style.textAlign = "center";p.style.minHeight = "20px";p.style.background = "#31313194";p.style.padding = "5px";p.style.borderRadius = "10px";p.style.color = "white";info1.appendChild(p);let tag = new CSS2DObject(info1);tag.name = "proLabel";tag.position.set(item.x, item.y, item.z);this.scene.add(tag);this.tagArray.push(tag);this.css2Renderer.domElement.style.position = "absolute";this.css2Renderer.domElement.style.top = "0";this.css2Renderer.domElement.style.pointerEvents = "none";document.getElementById("biaozhudian").appendChild(this.css2Renderer.domElement);});},clickBiaoji() {this.vector = new THREE.Vector3();this.screenVector = new THREE.Vector3();this.doc = document;this.activePoint = null;this.div = this.doc.getElementById("tip");this.raycaster = new THREE.Raycaster();this.mouse = new THREE.Vector2();this.tagObject = new THREE.Object3D();},/*** 鼠标点击触发**/onMouseDown(event) {if (event.buttons === 2 && this.pointIndex != null) {// 屏幕坐标转标准设备坐标this.vector.set((event.clientX / window.innerWidth) * 2 - 1,-(event.clientY / window.innerHeight) * 2 + 1,0);// 将标准设备坐标转为世界坐标this.vector.unproject(this.camera);this.raycaster = new THREE.Raycaster(this.camera.position,this.vector.sub(this.camera.position).normalize());let intersects = this.raycaster.intersectObjects([this.mesh]);if (intersects.length > 0) {this.activePoint = intersects[0].point;let point = this.toScreenPosition({point: this.activePoint,});this.div.style.left = point.x + "px";this.div.style.top = point.y + 20 + "px";}let xyz = this.activePoint;this.biaojiList[this.pointIndex].x = xyz.x;this.biaojiList[this.pointIndex].y = xyz.y;this.biaojiList[this.pointIndex].z = xyz.z;console.log("点击坐标-----", JSON.stringify(this.activePoint)); // 传给后台的参数}},toScreenPosition({ obj = null, point = null }) {point ? this.screenVector.set(...point) : this.screenVector.set();// 屏幕坐标系中心let widthHalf = this.renderer.getContext().canvas.width / 2;let heightHalf = this.renderer.getContext().canvas.height / 2;if (obj) {// 更新物体及其后代的全局变换obj.updateMatrixWorld();// 提取位置相关的分量this.screenVector.setFromMatrixPosition(obj.matrixWorld);}// 世界坐标转标准设备坐标。范围[-1,1]this.screenVector.project(this.camera);//标准设备坐标转屏幕坐标(2D)this.screenVector.x = this.screenVector.x * widthHalf + widthHalf;this.screenVector.y = -this.screenVector.y * heightHalf + heightHalf;return {x: this.screenVector.x,y: this.screenVector.y,};},// 陀螺仪setOrientationControls(e) {// 判断手机电脑端if (!e.alpha) {return;}this.isFull = true;this.dcontrols = new DeviceOrientationControls(this.camera, true);this.dcontrols.connect();this.dcontrols.update();window.removeEventListener("deviceorientation",this.setOrientationControls,true);},// 陀螺仪权限判断fullOrExit() {let _this = this;if (!_this.isFull) {try {console.log("浏览器UA---->", navigator.userAgent);if (navigator.userAgent.includes("iPhone") ||navigator.userAgent.includes("iPad") ||navigator.userAgent.includes("iPod") ||navigator.userAgent.includes("Macintosh")) {// 这是苹果设备上的浏览器console.log("这是苹果设备上的浏览器");if (window.DeviceOrientationEvent !== undefined &&typeof window.DeviceOrientationEvent.requestPermission ==="function") {window.DeviceOrientationEvent.requestPermission().then(function (response) {if (response == "granted") {window.addEventListener("deviceorientation",_this.setOrientationControls,true);}}).catch(function (error) {console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:",error);});} else {window.addEventListener("deviceorientation",_this.setOrientationControls,true);}} else {// 这不是苹果设备上的浏览器console.log("这不是苹果设备上的浏览器");window.addEventListener("deviceorientation",_this.setOrientationControls,true);}} catch (error) {console.error("监听事件处理程序出错:", error);}} else {if (_this.dcontrols) {_this.dcontrols.disconnect();_this.dcontrols.update();window.removeEventListener("deviceorientation",_this.setOrientationControls,true);} else {console.log("dcontrols 未创建");}_this.isFull = false;}},// 增删改查数据开始//删除标点delPoint(index) {this.pointIndex = null;this.biaojiList.splice(index, 1);this.biaoji();},//获取标点huoqu(index) {this.pointIndex = index;},//保存标点baocun(index) {this.pointIndex = null;this.biaoji();},//上传标点信息async submitPoint() {let FormData = new window.FormData();FormData.append("aerialId", this.aerialData.aerialId);FormData.append("markData", JSON.stringify(this.biaojiList));let res = await updateMark(FormData);if (res.data == 1) {this.$message.success("上传成功");console.log(res);}},//新增标点数据addPoint() {this.biaojiList.push({ title: "", x: "", y: "", z: "" });},// 增删改查数据结束},
};
</script><style rel="stylesheet/scss" lang="scss" scoped>
// ::v-deep#quanjing {overflow: hidden;width: 100vw;height: 100vh;touch-action: none;position: absolute;top: 0px;
}
.info1 {position: absolute;width: 40px;height: 40px;
}.info1 img {width: 40px;height: 40px;
}.info1 p {position: absolute;bottom: 100%;left: -70%;min-width: 100px;text-align: center;min-height: 20px;color: white;
}
.rig-list {position: absolute;right: 10px;top: 40px;img {width: 40px;height: 40px;margin-bottom: 10px;}#hangpaiIcon {z-index: 2;}
}
.addPoint {width: 750px;background: rgba(255, 255, 255, 0.453);min-height: 50px;position: absolute;top: 0;left: 50%;margin-left: -375px;
}
.row {width: 100%;overflow: hidden;margin-bottom: 5px;div {float: left;margin-left: 10px;}.inp {float: left;border: none;outline: none;width: 100px;margin-left: 10px;}
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/183855.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【刷题篇】动态规划(三)

文章目录 1、第 N 个泰波那契数2、三步问题3、使用最小花费爬楼梯4、解码方法5、不同路径6、不同路径 II 1、第 N 个泰波那契数 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c;请返回第 n 个泰波…

【数学】 4、向量的内积、外积、模长

文章目录 一、向量点乘&#xff08;内积&#xff09;1.1 几何意义1.2 点乘的代数定义&#xff0c;推导几何定义&#xff08;用于求向量夹角&#xff09;1.2.1 余弦定理 1.3 程序计算 二、向量叉乘&#xff08;外积&#xff09;2.1 几何意义 三、通俗理解内积和外积四、向量的模…

【网络编程】传输层——TCP协议

文章目录 TCP协议TCP协议格式窗口大小六个标志位确认应答机制超时重传机制连接管理机制三次握手四次挥手 流量控制滑动窗口拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结基于TCP的应用层协议TCP与UDP的对比 TCP相关实验CLOSE_WAIT状态实验TIME_WAIT状态实验TI…

gitlab数据备份和恢复

gitlab数据备份 sudo gitlab-rake gitlab:backup:create备份文件默认存放在/var/opt/gitlab/backups路径下&#xff0c; 生成1697101003_2023_10_12_12.0.3-ee_gitlab_backup.tar 文件 gitlab数据恢复 sudo gitlab-rake gitlab:backup:restore BACKUP1697101003_2023_10_12_…

Texlive安装

下载4.8G的iso文件 解压 或 装载后&#xff0c;以管理员身份运行(.bat)文件。 运行以下两句代码进行Texlive相关升级 tlmgr option repository otan tlmgr update --self --all 运行以下三行代码&#xff0c;检查是否安装成功 latex -v xelatex -v pdflatex -v 如果有异常…

Hundred Finance 攻击事件分析

背景知识 Hundred Finance 是 fork Compound 的一个借贷项目&#xff0c;在2023/04/15遭受了黑客攻击。攻击者在发起攻击交易之前执行了两笔准备交易占据了池子&#xff0c;因为发起攻击的前提是池子处于 empty 的状态&#xff08;发行的 hToken 数量为 0&#xff09;。 准备交…

技术分享 | 使用 cURL 发送请求

cURL 是一个通过 URL 传输数据的&#xff0c;功能强大的命令行工具。cURL 可以与 Chrome Devtool 工具配合使用&#xff0c;把浏览器发送的真实请求还原出来&#xff0c;附带认证信息&#xff0c;脱离浏览器执行&#xff0c;方便开发者重放请求、修改参数调试&#xff0c;编写脚…

【GO】项目import第三方的依赖包

目录 一、导入第三方包 1.执行命令 2.查看go环境变量参数 3.查看go.mod文件的变化情况 二、程序里如何import 1. import依赖包 2. 程序编写 本次学习go如果依赖第三方的包&#xff0c;并根据第三方的包提供的接口进行编程&#xff0c;这里需要使用go get命令。下面将go…

个性化联邦学习-综述

介绍阅读的三篇个性化联邦学习的经典综述文章 Three Approaches for Personalization with Applications to Federated Learning 论文地址 文章的主要内容 介绍了用户聚类&#xff0c;数据插值&#xff0c;模型插值三种个性化联邦学习的方法。 用户聚类&#xff1a; 目的&a…

快速解决mfc140u.dll丢失问题,找不到mfc140u.dll修复方法分享

在计算机使用过程中&#xff0c;我们可能会遇到各种问题&#xff0c;其中之一就是某些dll文件丢失。最近&#xff0c;我就遇到了一个关于mfc140u.dll丢失的问题。mfc140u.dll是Microsoft Foundation Class&#xff08;MFC&#xff09;库中的一个动态链接库文件&#xff0c;它包…

Tomcat,jdk下载配置(发布项目)

Tomcat&#xff0c;jdk下载&#xff0c; 远程连接 启动以下服务 高级设置 允许别人连接进来 网上搜索jdk下载即可 双击下一步即可 下一步 输入java&#xff0c;看有没有安装成功 这是安装成功的 Tomcat就可以安装了 和以上操作一样&#xff0c;在网上下载安装包&#xff0c;…

手把手教你搭建属于自己的服务器

最近总是想搭建自己的网站&#xff0c;奈何皮夹里空空如也&#xff0c;服务器也租不起&#xff0c;更别说域名了。于是我就寻思能否自己搭建个服务器&#xff0c;还不要钱呢&#xff1f; 还真行&#xff01;&#xff01;&#xff01; 经过几天的冲浪&#xff0c;我发现有两个…

SpringBoot文件上传

SpringBoot文件上传 上传文件是互联网中常常应用的场景之一&#xff0c;最典型的情况就是上传头像等&#xff0c;今天就带着带着大家做一个 Spring Boot 上传文件的小案例。 1、pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xml…

pytest中的pytest.ini

[pytest] filterwarnings ignore::DeprecationWarning addopts -v -s markers uat:1 smok:2 log_cli1 xfail_strict True filterwarnings ignore::DeprecationWarning 这个的功能就是 test_login.py::Test_login::test_login_correct_password PASSEDwarnings summary …

[答疑]大老二和德州扑克-属性值没变,状态怎么变了

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 邬俊杰 2023-10-31 21:20 课上说状态是由属性值决定的&#xff0c;比如身高>170算高&#xff0c;某人身高175&#xff0c;算高。如果规则改了&#xff0c;身高>180算高&#xf…

C语言——循环结构

C语言提供了while&#xff0c;do...while&#xff0c;for三种语句构成循环结构。循环语句是程序中的一个基本语句&#xff0c;在编程中&#xff0c;如果我们需要对某些操作对象进行相同的操作&#xff0c;那么&#xff0c;使用循环语句&#xff0c;就能让计算机反复执行&#x…

【渗透测试】垂直越权(高危)、水平越权(中危)

目录 一、简介1.1 水平越权&#xff08;中危&#xff09;1.2 垂直越权&#xff08;高危&#xff09;1.3 方便记忆方法 二、修复方案2.1 水平越权修复2.2 垂直越权修复 一、简介 1.1 水平越权&#xff08;中危&#xff09; 漏洞危害&#xff1a; 水平越权 是相同级别&#xff0…

修改iframe生成的pdf的比例

如图想要设置这里的默认比例 在iframe连接后面加上#zoom50即可&#xff0c;50是可以随便设置的&#xff0c;设置多少就是多少比例 <iframe src"name.pdf#zoom50" height"100%" width"100%"></iframe>

算法打卡01——求两数之和

题目&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你…

macOS 下 starUML 软件激活方案

starUML每次打开都弹出提示其实挺烦的&#xff0c;于是研究了一下如何 po 解(激活)它。记录一下方法以便以后使用。 我觉得这个软件很好用&#xff0c;大型项目的所有图我都是用这个软件画的。 直接上步骤&#xff01;先关掉starUML 1、安装 asar&#xff0c;以便可以打开 asa…