分享HTML显示2D/3D粒子时钟
效果截图
实现代码
线上体验:three.js+cannon.js Web 3D
<!DOCTYPE html>
<head>
<title>three.js+cannon.js Web 3D</title><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta name="renderer" content="webkit"><meta name="force-rendering" content="webkit"><meta http-equiv="X-UA-Compatible" content="IE=10,chrome=1"><meta data-rh="true" name="keywords" content="three.js,JavaScript,cannon.js"><meta data-rh="true" name="description" content="three.js+cannon.js Web 3D"><meta data-rh="true" property="og:title" content="THREE.JS and CANNON.JS"><meta data-rh="true" property="og:url" content=""><meta data-rh="true" property="og:description" content="three.js+cannon.js Web 3D"><meta data-rh="true" property="og:image" content=""><meta data-rh="true" property="og:type" content="article"><meta data-rh="true" property="og:site_name" content=""><link rel="icon" href=""><style>.fullscreen {margin: 0px;padding: 0px;width: 100vw;height: 100vh;overflow: hidden;}html, body {overflow: hidden;font-family: '宋体', sans-serif;color: #2f2f2f;}#threeImage{display: none;}</style><script type="importmap">{"imports": {"three": "https://cdn.jsdelivr.net/npm/three@0.162.0/+esm","three/addons/": "https://cdn.jsdelivr.net/npm/three@0.162.0/examples/jsm/","lil-gui": "https://threejsfundamentals.org/3rdparty/dat.gui.module.js","@tweenjs/tween.js": "https://cdn.jsdelivr.net/npm/@tweenjs/tween.js@23.1.1/dist/tween.esm.js","cannonjs": "https://cdn.bootcdn.net/ajax/libs/cannon.js/0.6.2/cannon.min.js"}}</script></head><body class="fullscreen"><div id="threeImage" ><img id="testImage" src="" crossorigin="anonymous"/></div><canvas></canvas><script type="module">import * as THREE from 'three';import * as TWEEN from '@tweenjs/tween.js';import { OrbitControls } from 'three/addons/controls/OrbitControls.js';import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';import { GUI } from 'lil-gui';import 'cannonjs';let container;let scene, camera, renderer, controls;let NEAR = 0.1, FAR = 1000;let light, ambient, stats, info, mouseX, mouseY;let SHADOW_MAP_WIDTH = 512;let SHADOW_MAP_HEIGHT = 512;let MARGIN = 0;let SCREEN_WIDTH = window.innerWidth;let SCREEN_HEIGHT = window.innerHeight - 2 * MARGIN;//物理世界let world;// 创建默认材质let defaultMaterial = null//创建材质let bMaterial = nulllet cube, tempCubes = new THREE.Object3D();let color = "#12345678";function setup() {setupScene();setupCamera();setupRenderer();setupLights();setPlane();setControls();initCannon();setupEventListeners();animate();addGUI();}function setupScene() {scene = new THREE.Scene();scene.fog = new THREE.Fog( 0x222222, 1000, FAR );scene.background = new THREE.Color(0xf5e6d3);}function setupCamera() {let res = SCREEN_WIDTH / SCREEN_HEIGHT;camera = new THREE.PerspectiveCamera(45, res, NEAR, FAR);//camera.up.set(0,0,1);//camera.position.set(0,30,20);//camera.position.z = 19;//camera.position.y = -45;camera.position.set(0, 30, 30)//camera.lookAt(0, 0, 0);//camera.lookAt(new THREE.Vector3(0, 0, 0));}let rotWorldMatrix;function rotateAroundWorldAxis(object, axis, radians) {//object:需要旋转的物体,axis:旋转中心轴向量,radians:rotWorldMatrix = new THREE.Matrix4();rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);rotWorldMatrix.multiply(object.matrix); // pre-multiplyobject.matrix = rotWorldMatrix;object.rotation.setFromRotationMatrix(object.matrix);}function setupRenderer() {renderer = new THREE.WebGLRenderer({ clearColor: 0x000000, clearAlpha: 1, antialias: true,// 抗锯齿logarithmicDepthBuffer: true // 使用Three进行加载模型时,总会遇到模型相接处或某些区域出现频闪问题或内容被相邻近元素覆盖掉的情况,对数缓存开启可解决,使用对数缓存});renderer.setSize(window.innerWidth, window.innerHeight);//renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );renderer.setClearColor( scene.fog.color, 1 );renderer.autoClear = false;renderer.shadowMapEnabled = true;renderer.shadowMapSoft = true;//开启阴影效果 设置阴影类型// BasicShadowMap 能够给出没有经过过滤的阴影映射 —— 速度最快,但质量最差。// PCFShadowMap 为默认值,使用Percentage-Closer Filtering (PCF)算法来过滤阴影映射。// PCFSoftShadowMap 和PCFShadowMap一样使用 Percentage-Closer Filtering (PCF)算法过滤阴影映射,但在使用低分辨率阴影图时具有更好的软阴影。// VSMShadowMap 使用Variance Shadow Map (VSM)算法来过滤阴影映射。当使用VSMShadowMap时,所有阴影接收者也将会投射阴影renderer.shadowMap.enabled = true;renderer.shadowMap.type = THREE.BasicShadowMaprenderer.shadowMap.autoUpdate = falserenderer.shadowMap.needsUpdate = true// 是否使用传统照明模式,默认为是,关闭传统照明模式即可模仿物理光照,光亮随距离可递减renderer.useLegacyLights = false;// 设置色调映射renderer.toneMapping = THREE.ACESFilmicToneMapping;// 曝光强度renderer.toneMappingExposure = 0.8renderer.outputEncoding = THREE.sRGBEncoding;container = document.createElement( 'div' );document.body.appendChild( container );//document.body.appendChild(renderer.domElement);//renderer.domElement.style.position = "relative";//renderer.domElement.style.top = MARGIN + 'px';container.appendChild( renderer.domElement );}function setPlane(){let planeGeometry = new THREE.PlaneGeometry(60, 20);let planeMaterial = new THREE.MeshBasicMaterial({color: 0xffffff});let plane = new THREE.Mesh(planeGeometry, planeMaterial);// 几何体绕着x轴旋转-90度plane.rotateX(-Math.PI/2);// 设置平面网格为接受阴影的投影面plane.receiveShadow = true;scene.add(plane);}function setupLights() {let ambientLight = new THREE.AmbientLight(0xffffff);ambientLight.castShadow = true;scene.add(ambientLight);// LIGHTSambient = new THREE.AmbientLight( 0xffffff );ambient.castShadow = true;scene.add( ambient );// 添加聚光灯1addSpotlight(50,50,50);// 添加聚光灯2addSpotlight(-50,50,50);// 添加聚光灯3addSpotlight(50,50,-50);// 添加聚光灯4addSpotlight(-50,50,-50);addLight();}function addLight(){light = new THREE.SpotLight( 0xffffff );light.position.set( 30, 30, 30 );light.target.position.set( 0, 0, 0 );light.castShadow = true;light.shadowCameraNear = 10;light.shadowCameraFar = 100;//camera.far;light.shadowCameraFov = 30;light.shadowMapBias = 0.0039;light.shadowMapDarkness = 0.5;light.shadowMapWidth = SHADOW_MAP_WIDTH;light.shadowMapHeight = SHADOW_MAP_HEIGHT;light.shadowCameraVisible = true;scene.add( light );}function setupEventListeners() {window.addEventListener("resize", onWindowResize);}function onDocumentMouseMove( event ) {mouseX = ( event.clientX - windowHalfX );mouseY = ( event.clientY - windowHalfY );}function onWindowResize( event ) {SCREEN_WIDTH = window.innerWidth;SCREEN_HEIGHT = window.innerHeight;renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );//camera.aspect = window.innerWidth / window.innerHeight;//renderer.setSize(window.innerWidth, window.innerHeight);camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;camera.updateProjectionMatrix();//controls.screen.width = SCREEN_WIDTH;//controls.screen.height = SCREEN_HEIGHT;camera.radius = ( SCREEN_WIDTH + SCREEN_HEIGHT ) / 4;}//let clock = new THREE.Clock()function render () {renderer.clear();renderer.render(scene, camera);controls.update();//let delta = clock.getDelta()//world.step(delta)// 更新物理世界 step ( dt , [timeSinceLastCalled] , [maxSubSteps=10] )// dt:固定时间戳(要使用的固定时间步长)// [timeSinceLastCalled]:自上次调用函数以来经过的时间// [maxSubSteps=10]:每个函数调用可执行的最大固定步骤数// * 设置更新物理世界world的步长timestep// * 这里选用60Hz的速度,即1.0 / 60.0world.step(1.0 / 60.0)//world.fixedStep()}function animate() {render();requestAnimationFrame(animate);}function addTexture(url){// 加载纹理const textureLoader = new THREE.TextureLoader()const texture = textureLoader.load(url)//textureLoader.load(url, (texture) => {// texture.mapping = THREE.EquirectangularReflectionMapping// scene.background = texture// scene.environment = texture// // 背景模糊强度// scene.backgroundBlurriness = 0.01//})return texture}function setControls(){controls = new OrbitControls( camera, renderer.domElement );controls.enableDamping = true;controls.rotateSpeed = 1.0;controls.zoomSpeed = 1.2;controls.panSpeed = 0.2;controls.noZoom = false;controls.noPan = false;controls.staticMoving = false;controls.dynamicDampingFactor = 0.3;const radius = 10;controls.minDistance = 0.0;controls.maxDistance = radius * 10;controls.enablePan = truecontrols.dampingFactor = 0.25controls.screenSpacePanning = falsecontrols.enableZoom = truecontrols.zoomScale = 10controls.minZoom = 1controls.maxZoom = 100//controls.minPolarAngle = 1 * -Math.PI / 180//controls.maxPolarAngle = 90 * Math.PI / 180//controls.minAzimuthAngle = 90 * -Math.PI / 180//controls.maxAzimuthAngle = 90 * Math.PI / 180//controls.keys = [ 65, 83, 68 ]; // [ rotateKey, zoomKey, panKey ]//controls.screen.width = SCREEN_WIDTH;//controls.screen.height = SCREEN_HEIGHT;/*// Trackball controlscontrols = new THREE.TrackballControls( camera, renderer.domElement );controls.rotateSpeed = 1.0;controls.zoomSpeed = 1.2;controls.panSpeed = 0.2;controls.noZoom = false;controls.noPan = false;controls.staticMoving = false;controls.dynamicDampingFactor = 0.3;var radius = 100;controls.minDistance = 0.0;controls.maxDistance = radius * 1000;//controls.keys = [ 65, 83, 68 ]; // [ rotateKey, zoomKey, panKey ]controls.screen.width = SCREEN_WIDTH;controls.screen.height = SCREEN_HEIGHT;*/}function addSpotlight (x,y,z){const spotLight2 = new THREE.SpotLight(0xffffff, 1)spotLight2.position.set(x, y, z)spotLight2.target.position.set( 0, 0, 0 )spotLight2.castShadow = truespotLight2.shadow.camera.near = 0.1spotLight2.shadow.camera.far = 30spotLight2.shadow.camera.fov = 30spotLight2.shadow.mapSize.width = 256spotLight2.shadow.mapSize.height = 256// 设置灯光 bias ,解决自阴影问题spotLight2.shadow.bias = -0.0008spotLight2.power = 1scene.add(spotLight2)// 使用辅助器对灯光和阴影进行调整//const cameraHelper = new THREE.SpotLightHelper(spotLight2)//scene.add(cameraHelper)}function initCannon() {// 初始化物理世界world = new CANNON.World()// 设置物理世界重力加速度 单位:m/s² 重力加速度x、y、z分量值,假设y轴竖直向上,这样重力就速度就是y方向负方向。world.gravity.set(0, -9.82, 0)// npm install cannon-es-debugger// 加入 cannon-es-debugger 可以展示模型的物理世界的轮廓// scene: 场景// 物理世界// 第三个参数为可选参数,其中的的onInit方法返回场景中的每个刚体和对应的物理世界的轮廓的three mesh// const cannonDebugger = CannonDebugger(scene, world)// const cannonDebugger = CannonDebugger(scene, world, {// onInit(body: CANNON.Body, mesh: THREE.Mesh) {// // // mesh.visible = true// console.log(body);// },// })// 还要在每帧更新调用中更新 Update the CannonDebugger meshes// cannonDebugger.update() // 创建默认材质defaultMaterial = new CANNON.Material('default')//创建足球材质bMaterial = new CANNON.Material('bMaterial')// 定义两种材质之间的摩擦因数和弹力系数 设置地面材质和小球材质之间的碰撞反弹恢复系数const defaultContactMaterial = new CANNON.ContactMaterial(defaultMaterial, bMaterial, {friction: 5,restitution: 0.5, //反弹恢复系数})// 把关联的材质添加到物理世界中world.addContactMaterial(defaultContactMaterial)// NaiveBroadphase Cannon 默认的算法。检测物体碰撞时,一个基础的方式是检测每个物体是否与其他每个物体发生了碰撞// GridBroadphase 网格检测。轴对齐的均匀网格 Broadphase。将空间划分为网格,网格内进行检测。// SAPBroadphase(Sweep-and-Prune) 扫描-剪枝算法,性能最好。// 默认为 NaiveBroadphase,建议替换为 SAPBroadphase// 碰撞算法world.broadphase = new CANNON.SAPBroadphase(world)//world.broadphase = new CANNON.NaiveBroadphase();// 创建一个物理世界的平面const planeShape = new CANNON.Plane()// 创建一个刚体const planeBody = new CANNON.Body({mass: 0, // 设置质量为0,不受碰撞的影响shape: planeShape,position: new CANNON.Vec3(0, 1, 0)})// 改变平面默认的方向,法线默认沿着z轴,旋转到平面向上朝着y方向// 设置刚体旋转(设置旋转X轴)旋转规律类似threejs 平面planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)// 将刚体添加到物理世界当中world.addBody(planeBody)const textureLoader = new THREE.TextureLoader();let texture = textureLoader.load('https://ts2.cn.mm.bing.net/th?id=OJ.nhM9wE4TnIlRRg');texture.wrapS = THREE.RepeatWrapping;texture.wrapT = THREE.RepeatWrapping;texture.repeat.set(1,1);texture.needsPMREMUpdate = truetexture.minFilter = THREE.LinearFiltertexture.magFilter = THREE.LinearFilterscene.environment = texture;let cubeGeometry = new THREE.BoxGeometry(1,1,1);// http://www.webgl3d.cn/pages/21003f/ PBR材质金属度、粗糙度以及环境贴图const cMaterial = new THREE.MeshPhysicalMaterial({ map: texture,side: THREE.DoubleSide,normalMap: texture,normalScale: new THREE.Vector2(1.1,1.1),specularMap: texture,specular: 0xffffff,shininess: 10,envMap: texture,color: color, metalness: 0.5,//像金属的程度, 非金属材料,如木材或石材,使用0.0,金属使用1.0。roughness: 0.5,//模型表面的光滑或者说粗糙程度,越光滑镜面反射能力越强,越粗糙,表面镜面反射能力越弱,更多地表现为漫反射。 clearcoat: 1,clearcoatRoughness: 0.01,envMapIntensity: 2.5,opacity: 0.5,transparent: true });cube = new THREE.Mesh(cubeGeometry,cMaterial);cube.position.x=0;cube.position.y=2;cube.position.z=0;cube.castShadow = true;scene.add(cube);}const calcMeshCenter = (group)=>{/*** 包围盒全自动计算:模型整体居中*/let box3 = new THREE.Box3()// 计算层级模型group的包围盒// 模型group是加载一个三维模型返回的对象,包含多个网格模型box3.expandByObject(group)// 计算一个层级模型对应包围盒的几何体中心在世界坐标中的位置let center = new THREE.Vector3()box3.getCenter(center)// console.log('查看几何体中心坐标', center);// 重新设置模型的位置,使之居中。group.position.x = group.position.x - center.xgroup.position.y = group.position.y - center.ygroup.position.z = group.position.z - center.z}const gui = new GUI({width: 280,title: 'Setting',autoPlace: true})var params = new function() {this.color = 0x123456;this.position = -35;this.visible = true;};function addGUI(){gui.addColor(params, "color").onChange(e => {cube.material.color.set(e);});gui.add(params, "position", -172, 28).onChange(e => {tempCubes.position.x = e});}const canvas = document.querySelector('canvas');const ctx = canvas.getContext('2d',{//频繁读取画布信息willReadFrequently: true});function initCanvasSize(){//canvas绘制的图形是位图,即栅格图像或点阵图像,当将它渲染到高清屏时,会被放大,每个像素点会用devicePixelRatio的平方个物理像素点来渲染,因此图片会变得模糊。//将画布尺寸设置为画板尺寸的window.devicePixelRatio倍canvas.width = window.innerWidth * window.devicePixelRatio;canvas.height = "150"//window.innerHeight * window.devicePixelRatio;}initCanvasSize();//获取[min,max]范围内的随机数function getRandom(min, max){return Math.floor(Math.random()*(max+1-min)+min);}//通过构造函数生成圆形颗粒class Particle{constructor(){//生成圆圈的半径const r = Math.min(canvas.width,canvas.height)/2;//画布的中心点const cx = canvas.width/2;const cy = canvas.height/2;//大圆上的随机角度并换算成弧度const rad = getRandom(0,360)*(Math.PI/180); //粒子的坐标this.x = cx + r*Math.cos(rad);this.y = cy + r*Math.sin(rad);//粒子的尺寸this.size = getRandom(1*window.devicePixelRatio,5*window.devicePixelRatio);}randomHexColor() { //随机生成十六进制颜色const hex = Math.floor(Math.random() * 16777216).toString(16); //生成ffffff以内16进制数while (hex.length < 6) { //while循环判断hex位数,少于6位前面加0凑够6位hex = '0' + hex;}return '#' + hex; //返回‘#'开头16进制颜色}draw(){ctx.beginPath();ctx.fillStyle = color;//ctx.fillStyle = this.randomHexColor();ctx.arc(this.x,this.y,this.size,0,2*Math.PI);ctx.fill();}moveTo(targetX,targetY){//设定一个缓动时间 500 ms毫秒//起始位置const startX = this.x;const startY = this.y;const duration = 500;//横向速度 目标位置 减去 当前位置 再除以运动的时间const xSpeed = (targetX - startX)/duration;//纵向速度 目标位置 减去 当前位置 再除以运动的时间const ySpeed = (targetY - startY)/duration;//起始时间const startTime = Date.now();//增加动画效果const _move = ()=>{//运动时间const t = Date.now() - startTime;//x方向运动距离 起始位置 加 速度乘以时间const x = startX + xSpeed * t;//y方向运动距离 起始位置 加 速度乘以时间const y = startY + ySpeed * t;//x,y缓动this.x = x;this.y = y;//超过设定时间取消运动if(t>=duration){// 赋值为目标位置this.x = targetXthis.y = targetY}//重新注册rafrequestAnimationFrame(_move)}//执行移动_move();}}//点数据组const partciles = [];//需要绘制的文本let text = null;//填充点数据for(let i = 0;i<1;i++){//画单个点const p = new Particle();//启动绘制//p.draw();//添加进数组partciles.push(p);}//清空画布function clear(){//清空画布ctx.clearRect(0,0,canvas.width,canvas.height);tempCtx.clearRect(0,0,tempCanvas.width,tempCanvas.height);// 将缓存 canvas 复制到旧的 canvas//ctx.drawImage(tempCanvas,0,0,canvas.width,canvas.height);}// 新建一个 canvas 作为缓存 canvasconst tempCanvas = document.createElement('canvas');// 设置缓冲画布的宽高tempCanvas.width = canvas.width; tempCanvas.height = canvas.height;const tempCtx = tempCanvas.getContext('2d');//绘制function draw(){//在每次绘制文本之前清空画布clear();// 开始绘制//let img = document.createElement('img');//let data = canvas.toDataURL();//img.src = data;// 缓存 canvas 绘制完成//tempCtx.drawImage(img,0,0);update();//遍历数组中的所有点partciles.forEach((p)=>{//绘制点p.draw();//p.moveTo(0,0);})//下一次绘制时注册RAF,重新绘制requestAnimationFrame(draw);}//生成文字function getText(){//得到当前时间 并做截取return new Date().toTimeString().substring(0,8);}//绘制文字function update(){//获得文本const newText = getText();if(newText == text){//相同就不绘制return;}text = newText;//绘制 文本const { width, height } = tempCanvas;if(drImage){// let temp = localStorage.getItem("threeImage");// CSSImageValue 或 HTMLCanvasElement 或 HTMLImageElement 或 HTMLVideoElement 或 ImageBitmap 或 OffscreenCanvas 或 SVGImageElement 或 VideoFrame//tempCtx.drawImage(document.getElementById("testImage"),0,0,tempCanvas.width,tempCanvas.height);tempCtx.fillStyle = "#000";tempCtx.textBaseline = "middle";tempCtx.font = `${100*window.devicePixelRatio}px '华文细黑', sans-serif`;//计算出绘制出的文本的宽度const textWidth = tempCtx.measureText(text).width;tempCtx.fillText(text,(width - textWidth)/3,height/2);//imageBox.appendChild(tempCanvas);}//拿到画布上黑色像素点的坐标const points = getPoints();//console.log(points);//如果数组长度小于文本坐标的长度 删除多余部分即可if(points.length < partciles.length){partciles.splice(points.length);}scene.remove(tempCubes); // 更新前删除旧数码显示tempCubes = null;tempCubes = new THREE.Object3D();for(let i=0;i<points.length;i++){let p = partciles[i];//粒子不足创建即可if(!p){p = new Particle();partciles.push(p);}const [x,y] = points[i];p.moveTo(x,y);let tempCube = cube.clone();tempCube.position.set((x/10),(y/10),(y/10));tempCubes.add(tempCube);}if(tempCubes){//calcMeshCenter(tempCubes);rotateAroundWorldAxis(tempCubes,new THREE.Vector3(1,0,0), Math.PI);tempCubes.position.set(-36,10,0);tempCubes.rotateX(-Math.PI/2);tempCubes.scale.set(0.5, 0.5, 0.5);//flipMesh(tempCubes);let position = new THREE.Vector3();let quaternion = new THREE.Quaternion();let scale = new THREE.Vector3();tempCubes.matrixWorld.decompose( position, quaternion, scale );tempCubes.updateMatrixWorld( true );scene.add(tempCubes);}}function flipMesh(object3D) {object3D.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));reverseWindingOrder(object3D);}function reverseWindingOrder(object3D) {// TODO: Something is missing, the objects are flipped alright but the light reflection on them is somehow brokenif (object3D.type === "Mesh") {var geometry = object3D.geometry;for (var i = 0, l = geometry.faces.length; i < l; i++) {var face = geometry.faces[i];var temp = face.a;face.a = face.c;face.c = temp;}var faceVertexUvs = geometry.faceVertexUvs[0];for (i = 0, l = faceVertexUvs.length; i < l; i++) {var vector2 = faceVertexUvs[i][0];faceVertexUvs[i][0] = faceVertexUvs[i][2];faceVertexUvs[i][2] = vector2;}geometry.computeFaceNormals();geometry.computeVertexNormals();}if (object3D.children) {for (var j = 0, jl = object3D.children.length; j < jl; j++) {reverseWindingOrder(object3D.children[j]);}}}function getPoints(){// 得到画布上制定范围内的像素点信息const { width, height, data} = tempCtx.getImageData(0,0,tempCanvas.width,tempCanvas.height);//console.log(data);const points = [];//像素点取稀释点const gap = 4;for(let i = 0; i< width;i+=gap){for(let j = 0; j< height;j+=gap){//通过行号、列号 计算像素点的下标const index = (i+j*width)*4;const r = data[index];const g = data[index+1];const b = data[index+2];const a = data[index+3];//判断是否透明if(r===0&&g===0&&b===0&&a===255){//console.log(r,g,b,a);points.push([i,j])}}}return points;}let imageBox = document.getElementById('threeImage')const loadImage = (url) => {return new Promise((resolve, reject) => {const img = new Image()img.onload = () => {resolve(img);}img.onerror = (err) => {reject(err)}img.src = urlimg.alt = "threeImage";})}function startDownload(url) {document.getElementById("testImage").src = url;document.getElementById("testImage").width = window.innerWidthdocument.getElementById("testImage").height = window.innerHeightloadImage(url).then((resImage)=>{imageReceived(resImage)}).catch((err)=>{throw new Error(err); // reject promise});//let imageURL = url;//let imageDescription = "three image";//downloadedImg = new Image();//downloadedImg.crossOrigin = "Anonymous";//downloadedImg.addEventListener("load", imageReceived, false);//downloadedImg.alt = imageDescription;//downloadedImg.src = imageURL;}let drImage = nullfunction imageReceived(downloadedImg) {const c = document.createElement("canvas");let context = c.getContext("2d");c.width = window.innerWidth || downloadedImg.width;c.height = window.innerHeight || downloadedImg.height;c.innerText = downloadedImg.alt;//downloadedImg.setAttribute('crossOrigin', 'anonymous');//context.drawImage(downloadedImg, 0, 0, c.width, c.height);//imageBox.appendChild(c);//确保图片被加载完成 使用drawImage绘制到画布上面drImage = downloadedImg; //画布已被跨域数据污染的操作try {localStorage.setItem("threeImage", c.toDataURL("image/png"));draw();// 复杂问题拆解成简单化问题// 画单个点,画很多个点,画很多个运动的点// 提升代码掌控能力,开发思维} catch (err) {console.error(`Error: ${err}`);}}//需要服务器端支持 CORS policy: No 'Access-Control-Allow-Origin' startDownload('https://ts2.cn.mm.bing.net/th?id=OJ.nhM9wE4TnIlRRg');setup();</script></body></html>
相关文章:
分享HTML显示2D/3D粒子时钟
效果截图 实现代码 线上体验:three.jscannon.js Web 3D <!DOCTYPE html> <head> <title>three.jscannon.js Web 3D</title><meta charset"utf-8"><meta name"viewport" content"widthdevice-width,ini…...
Java——IDEA使用
一、IDEA介绍 IntelliJ IDEA 是 JetBrains 公司开发的一款功能强大的集成开发环境(IDE),主要用于 Java 编程语言,但也支持多种其他语言和框架。由于其强大的功能和灵活性,IntelliJ IDEA 被广泛应用于软件开发领域&…...
高性能STL库 EASTL 、高性能JSON库
GitHub - electronicarts/EASTL: EASTL stands for Electronic Arts Standard Template Library. It is an extensive and robust implementation that has an emphasis on high performance. 兄弟们,对STL要求性能高的可以试试这个EASTL库!!…...
多通道采集器采样接口设计[进行中...]
1.技术问题 这是一个非常小的设计,完全不值得把它展示出来。但是因为这个接口设计关系到一些细微的配置和技术限制,仍然有一些细节需要处理,并且很容易出错,我们先把技术问题罗列一下: 多个传感器对应的多个逻辑通道…...
rapidjson使用中crash问题分析
问题 在使用rapidjson时,使用Document的Parse方法解析json字符串,程序crash。 分析 可以参考https://github.com/Tencent/rapidjson/issues/1269,由于rapidjson的内存分配器默认认为内存分配成功,没有对分配后做判空判断&#…...
TCP协议中的三次握手和四次挥手机制
TCP协议中的三次握手和四次挥手机制 TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,它的三次握手和四次挥手机制是建立和断开连接的关键步骤。 三次握手: 第一次…...
Linux双网卡默认路由的metric设置不正确,导致SSH连接失败问题定位
测试环境 VMware虚拟机 RockyLinux 9 x86_64 双网卡:eth0(访问外网): 10.206.216.92/24; eth1(访问内网) 192.168.1.4/24 问题描述 虚拟机重启后,SSH连接失败,提示"Connection time out",重启之前SSH连接还是正常的…...
Batch入门学习:从零开始掌握批处理脚本
目录 1. Batch脚本简介 1.1 什么是Batch脚本? 1.2 Batch脚本的历史 1.3 Batch脚本的应用场景 2. Batch脚本基本语法 2.1 注释 2.2 变量 2.3 常用命令 2.3.1 ECHO 2.3.2 PAUSE 2.3.3 CLS 2.3.4 GOTO 2.3.5 IF 2.3.6 FOR 2.4 参数传递 2.5 输入输出重…...
diffusion model(十八):diffusion model中negative prompt的工作机制
info个人博客主页http://myhz0606.com/article/ncsn 前置阅读: DDPM: http://myhz0606.com/article/ddpm classifier-guided:http://myhz0606.com/article/guided classifier-free guided:http://myhz0606.com/article/classi…...
Python | Leetcode Python题解之第200题岛屿数量
题目: 题解: class Solution:def dfs(self, grid, r, c):grid[r][c] 0nr, nc len(grid), len(grid[0])for x, y in [(r - 1, c), (r 1, c), (r, c - 1), (r, c 1)]:if 0 < x < nr and 0 < y < nc and grid[x][y] "1":self.d…...
利用圆上两点和圆半径求解圆心坐标
已知圆上两点P1,P2,坐标依次为 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) (x1,y1),(x2,y2),圆的半径为 r r r,求圆心的坐标。 假定P1,P2为任意两点,则两点连成线段的中点坐标是 x m i …...
从ChatGPT代码执行逃逸到LLMs应用安全思考
摘要 11月7日OpenAI发布会后,GPT-4的最新更新为用户带来了更加便捷的功能,包括Python代码解释器、网络内容浏览和图像生成能力。这些创新不仅开辟了人工智能应用的新境界,也展示了GPT-4在处理复杂任务方面的惊人能力。然而,与所有…...
Python入门-基础知识-变量
1.标识符与关键字 Python语言规定,标识符由字母、数字和下画线组成,且不允许以数字开头。合法的标识符可以 是student_1、 addNumber、num等,而3number、2_student等是不合法的标识符。在使用标识符时应注意以下几点。 (1)命名时应遵循见名知…...
设计模式原则——接口隔离原则
设计模式原则 设计模式示例代码库地址: https://gitee.com/Jasonpupil/designPatterns 接口隔离原则 要求程序员尽量将臃肿庞大的接口拆分为更小的和更具体的接口,让接口中只包含客户感兴趣的方法接口隔离原则的目标是降低类或模块之间的耦合度&…...
MySQL数据库——在Centos7环境安装
MySQL在Centos7环境安装 1.切换root用户 安装与卸载中,用户全部切换成为root,安装好后,普通用户也能使用 2.卸载不要的环境 要将自己环境中有关mysql的全都删除,避免安装过程中被影响 ps axj | grep mariadb 先检查是否有mari…...
怎样规避液氮容器内部结霜的问题
液氮容器内部结霜问题一直是我们在使用液氮储存罐时遇到的一个棘手难题。液氮的极低温度使得容器内部很容易产生结霜现象,这不仅影响了容器的正常使用,还可能对内部样品或设备造成损坏。因此,如何有效规避液氮容器内部结霜问题成为了每个使用…...
冶金工业5G智能工厂工业物联数字孪生平台,推进制造业数字化转型
冶金工业5G智能工厂工业物联数字孪生平台,推进制造业数字化转型。传统生产方式难以满足现代冶金工业的发展需求,数字化转型成为必然趋势。通过引入5G、工业物联网和数字孪生等先进技术,冶金工业可以实现生产过程智能化、高效化和绿色化&#…...
一文入门机器学习参数调整实操
作者前言: 通过向身边的同事大佬请教之后,大佬指点我把本文的宗旨从“参数调优”改成了“参数调整”。实在惭愧,暂时还没到能“调优”的水平,本文只能通过实操演示“哪些操作会对数据训练产生影响”,后续加深学习之后,…...
基于51单片机的银行排队呼叫系统设计
一.硬件方案 本系统是以排队抽号顺序为核心,客户利用客户端抽号,工作人员利用叫号端叫号;通过显示器及时显示当前所叫号数,客户及时了解排队信息,通过合理的程序结构来执行排队抽号。电路主要由51单片机最小系统LCD12…...
JXCategoryView的使用总结
一、初始化 -(JXCategoryTitleView *)categoryView{if (!_categoryView) {_categoryView [[JXCategoryTitleView alloc] init];_categoryView.delegate self;_categoryView.titleDataSource self;_categoryView.averageCellSpacingEnabled NO; //是否平均分配项目之间的间…...
Centos9 安装VBox增强功能问题
安装步骤 更新gcc 首先手动更新gcc,防止无法兼容最新版本的内核,我这里将gcc 11更新到gcc 13 1.首先更新当前gcc和支持 yum install -y gcc gcc-c 2.下载新版本gcc压缩包 wget http://ftp.gnu.org/gnu/gcc/gcc-13.1.0/gcc-13.1.0.tar.gz 解压到usr ta…...
【JVM】Java虚拟机运行时数据分区介绍
JVM 分区(运行时数据区域) 文章目录 JVM 分区(运行时数据区域)前言1. 程序计数器2. Java 虚拟机栈3. 本地方法栈4. Java 堆5. 方法区6. 运行时常量池7. 直接内存 前言 之前在说多线程的时候,提到了JVM虚拟机的分区内存…...
大数据面试题之Kafka(2)
目录 Kafka的工作原理? Kafka怎么保证数据不丢失,不重复? Kafka分区策略 Kafka如何尽可能保证数据可靠性? Kafka数据丢失怎么处理? Kafka如何保证全局有序? 生产者消费者模式与发布订阅模式有何异同? Kafka的消费者组是如何消费数据的 Kafka的…...
前端面试题(基础篇十一)
一、DOCTYPE 的作用是什么? <!DOCTYPE> 声明一般位于文档的第一行,它的作用主要是告诉浏览器以什么样的模式来解析文档。一般指定了之后会以标准模式来进行文档解析,否则就以兼容模式进行解析。在标准模式下,浏览器的解析规…...
【论文阅读】Answering Label-Constrained Reachability Queries via Reduction Techniques
Cai Y, Zheng W. Answering Label-Constrained Reachability Queries via Reduction Techniques[C]//International Conference on Database Systems for Advanced Applications. Cham: Springer Nature Switzerland, 2023: 114-131. Abstract 许多真实世界的图都包含边缘标签…...
Git Flow 工作流学习要点
Git Flow 工作流学习要点 Git Flow — 流程图Git Flow — 操作指令优点:缺点:Git Flow 分支类型Git Flow 工作流程简述关于 feature 分支关于 Release 分支关于 hotfix 分支 总结 Git Flow — 流程图 图片来源:https://nvie.com/posts/a-succ…...
blender 快捷键 常见问题
一、快捷键 平移视图:Shift 鼠标中键旋转视图:鼠标中键缩放视图:鼠标滚动框选放大模型:Shift B线框预览和材质预览切换:Shift Z 二、常见问题 问题:导入模型成功,但是场景中看不到。 解…...
HTTP详解:TCP三次握手和四次挥手
一、TCP协议概述 TCP协议是互联网协议栈中传输层的核心协议之一,它提供了一种可靠的数据传输方式,确保数据包按顺序到达,并且没有丢失或重复。TCP的主要特点包括: 面向连接:TCP在传输数据之前需要建立连接。可靠传输&…...
详解HTTP:有了HTTP,为何需要WebSocket?
在日常生活中,HTTP 常用于请求数据。例如,当你打开一个天气预报网站时,浏览器会发送一个 HTTP 请求到服务器,请求当前的天气数据,服务器返回响应,浏览器解析并显示这些数据。 但是,当涉及到需要…...
Spring Boot 启动流程是怎么样的
引言 SpringBoot是一个广泛使用的Java框架,旨在简化基于Spring框架的应用程序的开发过程。在这篇文章中,我们将深入探讨SpringBoot应用程序的启动流程,了解其背后的机制。 Spring Boot 启动概览 SpringBoot应用程序的启动通常从一个包含 m…...
商丘做网站汉狮网络/武汉久都seo
Description 某工厂发现厂里的机器在生产产品时要消耗大量的原材料,也就是说,有大量的原材料变成了废物。因此厂里想找出消耗原材料最大的一条生产线路进行改造,以降低成本。厂里的生产线路是一个有向无环网络,有N台机器分别代表…...
找别人做网站注意事项/如何做好网络营销推广
返回:贺老师课程教学链接 【项目1-程序的多文件组织】请将下面的程序中的代码分别保存在两个文件中,利用多文件的方式组织。 * main.c(也可以是别的名字) #include <stdio.h> int max(int x,int y); int min(int x,int y…...
怎样做海外淘宝网站/武汉网站营销seo方案
计算机硬件系统实验报告PAGEPAGE 1计算机硬件系统实验报告RISC模型微处理器设计学 号:姓 名: 陆 二 庆指导教师: 陈智勇 老师专 业: 计算机应用技术日期:2006-10-171 实验题目设计一台RISC模型机…...
网站建设无锡/口碑营销的产品
在发送通知邮件的时候, 假如可以有漂亮的邮件模板就更好了,但是出于安全的原因, 邮件一般不支持 link 或者 style 样式,只能通过内联的方式。找到了 The Automatic CSS Inliner Tool。有了自动转换工具,那就简单了。首…...
做图文链接网站/一个新手如何推销产品
Windows 10系统是微软独立发布的最后一个Windows版本,下一代Windows都将作为更新形式出现,因此,WIN10都会开启自动更新,当我们使用电脑WIN10的时候,老是提示我们需要更新系统,很烦人,那么怎么关…...
西安网站维护 策划/学seo网络推广
IIS添加MIME类型: 文件扩展名 MIME类型 .svg image/svgxml .woff application/x-font-woff .woff2 application/x-font-woff .eot application/vnd.ms-fontobject .otf application/octet-stream .ttf applic…...