当前位置: 首页 > news >正文

uniapp vue3 微信小程序使用 three-platformize 加载本地和网络字体文件

tip:本地字体文件需自行准备,替换引入路径即可。

  1 <template>
  2   <view class="container">
  3     <canvas
  4       type="webgl"
  5       id="mycanvas"
  6       canvas-id="mycanvas"
  7       class="canvas"
  8     ></canvas>
  9 
 10     <view class="control-panel">
 11       <view class="item">
 12         <text>X轴位置</text>
 13         <slider
 14           style="width: 100%"
 15           :value="positionX * 10"
 16           show-value
 17           @change="updateFont($event, 'positionX')"
 18         />
 19         <text>{{ positionX }}</text>
 20       </view>
 21 
 22       <view class="item">
 23         <text>Y轴位置</text>
 24         <slider
 25           style="width: 100%"
 26           :value="positionY * 10"
 27           show-value
 28           @change="updateFont($event, 'positionY')"
 29         />
 30         <text>{{ positionY }}</text>
 31       </view>
 32 
 33       <view class="item">
 34         <text>Z轴位置</text>
 35         <text>{{ positionZ }}</text>
 36         <slider
 37           style="width: 100%"
 38           :value="positionZ * 10"
 39           show-value
 40           @change="updateFont($event, 'positionZ')"
 41         />
 42       </view>
 43 
 44       <view class="item">
 45         <text>字体颜色</text>
 46         <button @click="changeColor">随机颜色</button>
 47       </view>
 48 
 49       <view class="item">
 50         <text>文字厚度</text>
 51         <slider
 52           style="width: 100%"
 53           :value="fontHeight * 10"
 54           show-value
 55           @change="updateFont($event, 'fontHeight')"
 56         />
 57         <text>{{ fontHeight }}</text>
 58       </view>
 59 
 60       <view class="item">
 61         <text>透明度</text>
 62         <slider
 63           style="width: 100%"
 64           :value="opacity * 100"
 65           show-value
 66           @change="updateFont($event, 'opacity')"
 67         />
 68         <text>{{ opacity }}</text>
 69       </view>
 70     </view>
 71   </view>
 72 </template>
 73 
 74 <script setup>
 75 import { ref, onMounted, toRaw, onUnmounted, getCurrentInstance } from "vue";
 76 import * as THREE from "three-platformize";
 77 import { WechatPlatform } from "three-platformize/src/WechatPlatform";
 78 import { FontLoader } from "three-platformize/examples/jsm/loaders/FontLoader";
 79 import { TextGeometry } from "three-platformize/examples/jsm/geometries/TextGeometry";
 80 import fontJson from "./TmonMonsoriBlack_Regular.json"; // 本地字体文件
 81 // const fontJsonUrl =
 82 //   "https://cdn.jsdelivr.net/npm/three@0.147.0/examples/fonts/helvetiker_regular.typeface.json"; // 网络字体文件
 83 let scene = null;
 84 let camera = null;
 85 let renderer = null;
 86 let cubeMesh = null; // 先用立方体,小程序文字纹理兼容性极高,极易报错
 87 let textMesh = null; // 再用文字,小程序文字纹理兼容性极高,极易报错
 88 let animationId = null;
 89 const instance = getCurrentInstance();
 90 const canvas = ref(null);
 91 
 92 const positionX = ref(0);
 93 const positionY = ref(0);
 94 const positionZ = ref(0);
 95 const fontHeight = ref(1);
 96 const opacity = ref(1);
 97 const fontColor = ref("#ffffff");
 98 // 初始化 Canvas,并返回画布和尺寸
 99 const initCanvas = () => {
100   return new Promise(resolve => {
101     uni
102       .createSelectorQuery()
103       .in(instance)
104       .select("#mycanvas")
105       .node()
106       .exec(res => {
107         canvas.value = res[0].node;
108         resolve({
109           canvas: canvas.value,
110           width: 300,
111           height: 300,
112         });
113       });
114   });
115 };
116 // 初始化 Three.js 场景、相机、渲染器和灯光
117 async function initThree() {
118   const { width, height } = await initCanvas();
119   const platform = new WechatPlatform(toRaw(canvas.value));
120   THREE.PLATFORM.set(platform);
121 
122   scene = new THREE.Scene();
123   scene.background = new THREE.Color(0x1a1a1a);
124 
125   camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
126   camera.position.set(0, 0, 10);
127 
128   const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
129   scene.add(ambientLight);
130   const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
131   directionalLight.position.set(5, 5, 5);
132   scene.add(directionalLight);
133 
134   // 关键:不手动传 context!让 three-platformize 自己处理,彻底消灭 document 报错
135   renderer = new THREE.WebGLRenderer({ antialias: true });
136   renderer.setSize(width, height);
137 }
138 
139 // 立方体:100%稳定不报错
140 function create3DCube() {
141   const geometry = new THREE.BoxGeometry(2, 2, 2);
142   const material = new THREE.MeshStandardMaterial({
143     color: fontColor.value,
144     transparent: true,
145     opacity: opacity.value,
146   });
147   cubeMesh = new THREE.Mesh(geometry, material);
148   scene.add(cubeMesh);
149 }
150 // 文字
151 function create3DFont() {
152   // 3D文字
153   const loader = new FontLoader();
154   // 本地字体加载方式
155   const font = loader.parse(fontJson);
156   // "https://cdn.jsdelivr.net/npm/three@0.147.0/examples/fonts/helvetiker_regular.typeface.json",
157   const textGeo = new TextGeometry("JS哈哈文本", {
158     font,
159     size: 1,
160     height: 1,
161     curveSegments: 12,
162   });
163   const textMaterial = new THREE.MeshStandardMaterial({
164     color: fontColor.value,
165     metalness: 0.4,
166     roughness: 0.2,
167     transparent: true,
168     opacity: opacity.value,
169   });
170   console.log(textMaterial, "textMaterial");
171   console.log(textGeo, "textGeo");
172 
173   textMesh = new THREE.Mesh(textGeo, textMaterial);
174   textMesh.position.x = -3.5;
175   scene.add(textMesh);
176   // 网络字体加载方式
177   // loader.load(
178   //   // "https://cdn.jsdelivr.net/npm/three@0.147.0/examples/fonts/helvetiker_regular.typeface.json",
179   //   fontJsonUrl,
180   //   font => {
181   //     const textGeo = new TextGeometry("JS测试文本", {
182   //       font,
183   //       size: 1,
184   //       height: 1,
185   //       curveSegments: 12,
186   //     });
187   //     const textMaterial = new THREE.MeshStandardMaterial({
188   //       color: fontColor.value,
189   //       metalness: 0.4,
190   //       roughness: 0.2,
191   //       transparent: true,
192   //       opacity: opacity.value,
193   //     });
194   //     console.log(textMaterial, "textMaterial");
195   //     console.log(textGeo, "textGeo");
196 
197   //     textMesh = new THREE.Mesh(textGeo, textMaterial);
198   //     textMesh.position.x = -3.5;
199   //     scene.add(textMesh);
200   //   },
201   // );
202 }
203 // 更新字体属性和位置
204 function updateFont(e, prop) {
205   const { value } = e.detail;
206   console.log(prop, value);
207   // eslint-disable-next-line curly
208   if (prop === "positionX") positionX.value = value / 10;
209   // eslint-disable-next-line curly
210   else if (prop === "positionY") positionY.value = value / 10;
211   // eslint-disable-next-line curly
212   else if (prop === "positionZ") positionZ.value = value / 10;
213   // eslint-disable-next-line curly
214   else if (prop === "fontHeight") fontHeight.value = value / 10;
215   // eslint-disable-next-line curly
216   else if (prop === "opacity") opacity.value = value / 100;
217   if (prop === "fontColor" || prop === "opacity" || prop === "fontHeight") {
218     // 文字属性变化时,更新文字材质
219     if (textMesh) {
220       textMesh.material.color.set(fontColor.value);
221       textMesh.material.opacity = opacity.value;
222       const s = fontHeight.value;
223       textMesh.scale.set(s, s, s);
224     }
225 
226     return;
227   }
228   // 位置属性变化时,更新立方体位置
229   cubeMesh.position.set(positionX.value, positionY.value, positionZ.value);
230   cubeMesh.material.opacity = opacity.value;
231   const s = fontHeight.value;
232   cubeMesh.scale.set(s, s, s);
233 }
234 // 随机颜色
235 function changeColor() {
236   const color = new THREE.Color(Math.random() * 0xffffff);
237   fontColor.value = `#${color.getHexString()}`;
238   cubeMesh.material.color.set(color);
239   textMesh.material.color.set(color);
240 }
241 // 动画循环
242 function animate() {
243   animationId = canvas.value.requestAnimationFrame(animate);
244   // eslint-disable-next-line curly
245   if (cubeMesh) cubeMesh.rotation.y += 0.01;
246   renderer.render(scene, camera);
247 }
248 
249 onMounted(async () => {
250   await initThree();
251   create3DCube(); // 先用立方体,小程序文字纹理兼容性极高,极易报错
252   create3DFont(); // 再用文字,小程序文字纹理兼容性极高,极易报错
253   animate();
254 });
255 
256 onUnmounted(() => {
257   // eslint-disable-next-line curly
258   if (animationId) canvas.value.cancelAnimationFrame(animationId);
259   renderer?.dispose();
260   canvas.value = null;
261 });
262 </script>
263 
264 <style scoped>
265 .container {
266   width: 100%;
267   height: 100vh;
268 }
269 .canvas {
270   width: 100%;
271   height: 60vh;
272 }
273 .control-panel {
274   padding: 20rpx;
275   background: #fff;
276 }
277 .item {
278   margin: 20rpx 0;
279   display: flex;
280   align-items: center;
281   justify-content: space-between;
282 }
283 </style>

 

http://www.jsqmd.com/news/763322/

相关文章:

  • 如何深度优化AMD Ryzen处理器?免费硬件调试工具SMUDebugTool终极指南
  • 2026最新零基础瑜伽普拉提教练培训学院推荐!广东优质权威榜单发布,专业靠谱广州教练培训机构甄选 - 博客万
  • Gemini-CLI:在终端无缝集成AI助手的命令行工具详解
  • 防划痕、更省心:2026年食品级夹爪供应商盘点 - 品牌2026
  • DC综合中set_fix_multiple_port_nets命令的实战避坑:一个直连线警告引发的布局布线思考
  • 2026年4月最好的防火涂料供货厂家实力,市场靠谱的防火涂料生产厂家推荐,防火涂料——防火涂层致密,隔绝氧气 - 品牌推荐师
  • 如何用KMS_VL_ALL_AIO智能激活工具永久激活Windows和Office
  • 2026年4月最新到店体验:青岛婚纱照到底哪家好 - 江湖评测
  • 3步快速退出Windows预览版:OfflineInsiderEnroll终极指南
  • 2026年山东断桥铝门窗与系统阳光房选购指南 - 年度推荐企业名录
  • 替代1.85mm公头带铠甲接3506电缆精密连接器
  • 机床装上“智慧大脑”,老旧设备也能玩转工业互联
  • 2026年福建磨粉设备采购指南:小型磨粉机厂家对标与高效出粉率方案 - 年度推荐企业名录
  • 用STM32F4和CODESYS V3.5,我手搓了一个低成本PLC(附完整工程源码)
  • 按面积选空调最容易踩的 5 个坑:京东自营选错匹数到底有多伤钱 - 博客万
  • 超新星IIP光变曲线特征与CSM相互作用研究
  • SD-PPP:将Photoshop打造成AI绘画工作室的开源革命
  • 零基础入门Matlab绘图:借助快马AI生成可交互代码学习案例
  • 别再手动修线了!巧用Allegro的Slide etch功能,移动器件时让导线自动优化
  • 2026年山东断桥铝门窗与系统阳光房选购完全指南:泰安峰睿门窗深度评测与避坑秘诀 - 年度推荐企业名录
  • 保姆级教程:用Python脚本将JD9365A初始化代码一键转为RK3568设备树格式
  • TegraRcmGUI完整指南:如何三步解锁Switch的终极潜能?
  • 微信聊天记录本地化分析与数据主权实践
  • 终极音频解放方案:qmcdump完整解密QQ音乐加密文件指南
  • 基于AI利率预期模型的债市重定价机制分析:加息概率上修与降息路径延后的结构性演化
  • 2026年山东断桥铝门窗与系统阳光房选购指南:峰睿门窗深度评测 - 年度推荐企业名录
  • 告别工控机+PLC组合?用ZYNQ 7020 SoC打造一体化多轴运动控制器实战
  • 保姆级教程:在STM32CubeIDE工程里集成Micro-ROS(Humble版)
  • 2026年江苏面粉加工设备采购指南:源头厂家直供与品牌深度横评 - 年度推荐企业名录
  • AI提示词工程:45个场景化模板如何重塑创意工作流