前端小玩意兒:用three.js開發的手機太空穿越VR游戲,特效非常猛

来源:https://www.cnblogs.com/coderhf/archive/2020/05/19/12917297.html
-Advertisement-
Play Games

hello,今天給大家用three.js開發了一個手機太空穿越VR游戲,確實不容易,小編的頭髮又少了一大截。Ok,廢話少說,先看效果。 效果圖 首頁index.html <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> < ...


hello,今天給大家用three.js開發了一個手機太空穿越VR游戲,確實不容易,小編的頭髮又少了一大截。Ok,廢話少說,先看效果。

效果圖

首頁index.html

<!DOCTYPE html>
<html lang="en" >

<head>
<meta charset="UTF-8">
<title>Three.js Mobile VR Sonic</title>



<link rel="stylesheet" href="css/style.css">


</head>

<body>

<script src="js/jquery-1.12.4.min.js"></script>
<script src="js/d3.v4.min.js"></script>
<script src="js/three.min.js"></script>
<script src="js/GLTFLoader.js"></script>


<script src="js/TweenMax.min.js"></script>
<script src="js/CSSPlugin.min.js"></script>
<script src="js/EasePack.min.js"></script>



<script src='js/AssimpJSONLoader.js'></script>
<script src="js/DeviceOrientationControls.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/StereoEffect.js"></script>
<script src="js/tween.min.js"></script>
<script src="js/dat.gui.min.js"></script>
<!-- glowing effect scripts -->
<script src="js/EffectComposer.js"></script>
<script src="js/RenderPass.js"></script>
<script src="js/MaskPass.js"></script>
<script src="js/ShaderPass.js"></script>
<script src="js/CopyShader.js"></script>
<script src="js/FXAAShader.js"></script>
<script src="js/ConvolutionShader.js"></script>
<script src="js/LuminosityHighPassShader.js"></script>
<!-- unreal bloom -->
<script src="js/UnrealBloomPass.js"></script>


    <!-- VR Button -->
    <button id='VR' class='button toggleVR' onclick='toggleVR()' title='Toggle VR Mode for Mobile Devices Only'>
      <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 62.7 52.375" enable-background="new 0 0 62.7 41.9" xml:space="preserve"><path d="M53.4,5.5h-44c-2.1,0-3.7,1.7-3.7,3.7v22.6c0,2.1,1.7,3.7,3.7,3.7h13.4c1.1,0,2.1-0.6,2.5-1.6l3-7.5c1.2-2.6,4.9-2.5,6,0.1  l2.6,7.3c0.4,1,1.4,1.7,2.5,1.7h13.9c2.1,0,3.7-1.7,3.7-3.7V9.3C57.2,7.2,55.5,5.5,53.4,5.5z M20.4,27c-3.2,0-5.7-2.6-5.7-5.7  s2.6-5.7,5.7-5.7s5.7,2.6,5.7,5.7S23.6,27,20.4,27z M42.4,27c-3.2,0-5.7-2.6-5.7-5.7s2.6-5.7,5.7-5.7s5.7,2.6,5.7,5.7  S45.6,27,42.4,27z"/><text x="0" y="56.9" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Nick Bluth</text><text x="0" y="61.9" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
    </button>

    <div id="info">SUPER!</div>
    <audio id="bflat" src="sonic_ring_sound.mp3"></audio>
  
  

    <script  src="js/index.js"></script>




</body>

</html>

 

核心代碼js

//===================================================== full screen
var requestFullscreen = function(ele) {
  if (ele.requestFullscreen) {
    ele.requestFullscreen();
  } else if (ele.webkitRequestFullscreen) {
    ele.webkitRequestFullscreen();
  } else if (ele.mozRequestFullScreen) {
    ele.mozRequestFullScreen();
  } else if (ele.msRequestFullscreen) {
    ele.msRequestFullscreen();
  } else {
    console.log("Fullscreen API is not supported.");
  }
};
var exitFullscreen = function(ele) {
  if (ele.exitFullscreen) {
    ele.exitFullscreen();
  } else if (ele.webkitExitFullscreen) {
    ele.webkitExitFullscreen();
  } else if (ele.mozCancelFullScreen) {
    ele.mozCancelFullScreen();
  } else if (ele.msExitFullscreen) {
    ele.msExitFullscreen();
  } else {
    console.log("Fullscreen API is not supported.");
  }
};
//===================================================== add canvas
let renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xd8e7ff, 0);
document.body.appendChild(renderer.domElement);
//===================================================== resize
window.addEventListener("resize", function() {
  let width = window.innerWidth;
  let height = window.innerHeight;
  renderer.setSize(width, height);
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
});
//===================================================== add Scene
let scene = new THREE.Scene();
//===================================================== add Camera
let camera = new THREE.PerspectiveCamera(
  45,
  window.innerWidth / window.innerHeight,
  1,
  10000
);
let cameraTarget = new THREE.Vector3(0, 0, 0);
//===================================================== add Group
group = new THREE.Group();
scene.add(group);
//===================================================== add Controls
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
//How far you can orbit vertically, upper and lower limits. The maximum is Pi / 2 (90deg). You wont see below the below the line of the horizon
controls.maxPolarAngle = Math.PI / 2.1;
//===================================================== add VR
renderer.setPixelRatio(window.devicePixelRatio); //VR
effect = new THREE.StereoEffect(renderer); //VR
effect.setSize(window.innerWidth, window.innerHeight); //VR

var VR = false;
function toggleVR() {
  if (VR) {
    VR = false;
    controls = new THREE.OrbitControls(camera, renderer.domElement);
  } else {
    VR = true;
    controls = new THREE.DeviceOrientationControls(camera);
    requestFullscreen(document.documentElement);
  }
  renderer.setSize(window.innerWidth, window.innerHeight);
}
//===================================================== curve points exported from blender using python
var points = [
  /*[2.873088836669922, 1.886053442955017, 2.807666063308716] ,
[2.8677191734313965, 1.901498556137085, 0.9702248573303223] ,
[4.261392116546631, 1.1370995044708252, 2.199495315551758] ,
[5.4436726570129395, -0.46564579010009766, 1.1223225593566895] ,
[4.683673858642578, -5.823459148406982, 0.29172343015670776] ,
[4.683673858642578, -5.823459148406982, 0.29172343015670776] ,
[1.0320820808410645, -8.896514892578125, -0.25123584270477295] ,
[-6.009271621704102, -5.854086875915527, -1.619685411453247] ,
[-5.8719940185546875, 1.664353609085083, 1.4598760604858398] ,
[-3.664644718170166, 5.1350908279418945, 0.8891280889511108] ,
[0.3946661949157715, 9.023353576660156, 2.472759246826172] ,
[6.545413017272949, 7.975126266479492, 4.807941913604736] ,
[9.380702018737793, 3.8875434398651123, -1.2000198364257812] ,
[3.980130672454834, -3.3519601821899414, -1.5907882452011108] ,
[0.10054226964712143, -4.0724077224731445, -2.6255977153778076] ,
[0.11306309700012207, -4.062130451202393, -2.625786781311035] ,
[-1.5915921926498413, 2.2223830223083496, 1.9054492712020874] ,
[-0.7800552845001221, 2.729933738708496, 2.711679458618164] ,
[2.2788195610046387, 1.5061609745025635, 3.6167585849761963] ,
[3.330962896347046, -1.753382682800293, 3.5841569900512695] ,
[3.3268532752990723, -1.7394065856933594, 3.584031343460083] ,
[1.8003381490707397, -2.6099541187286377, 4.496334552764893] ,
[1.809736728668213, -2.6042258739471436, 4.490817546844482] ,
[-1.5139055252075195, -2.2446377277374268, 3.513293743133545] ,
[-1.5139055252075195, -2.2446377277374268, 3.513293743133545] ,
[-1.5139055252075195, -2.2446377277374268, 1.7960444688796997] ,
[-1.9393141269683838, -0.623103678226471, 1.3167498111724854] ,
[-1.5, 0.0, 0.0] ,
[-1.0, 1.0, 0.0] ,
[1.0, 1.0, 0.0] ,
[1.5, 0.0, 0.0] ,*/

  [1.8117204904556274, 5.987488269805908, 0.29106736183166504],
  [6.005367279052734, 1.7647128105163574, -1.5591322183609009],
  [1.435487985610962, -6.016839504241943, 2.1336286067962646],
  [-4.118395805358887, -6.886471271514893, -0.7294682264328003],
  [-4.732693195343018, 3.405961036682129, 3.1304938793182373],
  [8.304193496704102, 7.593861103057861, 0.3412821292877197],
  [8.038525581359863, -4.391696453094482, 2.687108278274536],
  [1.488401174545288, -9.993440628051758, -2.2956111431121826],
  [-5.277090549468994, -8.481210708618164, -0.719127893447876],
  [-7.250330448150635, -0.9653520584106445, -0.3089699447154999],
  [-6.526705741882324, 5.678538799285889, 0.15560221672058105],
  [-0.885545015335083, 6.678538799285889, 1.5724562406539917],
  [1.614454984664917, 5.678538799285889, 0.24559785425662994],
  [1.8117204904556274, 5.987488269805908, 0.29106736183166504]
];

var scale = 5;

//Convert the array of points into vertices
for (var i = 0; i < points.length; i++) {
  var x = points[i][0] * scale;
  var y = points[i][1] * scale;
  var z = points[i][2] * scale;
  points[i] = new THREE.Vector3(x, z, -y);
}

//Create a path from the points
var carPath = new THREE.CatmullRomCurve3(points);
var radius = 0.25;

var geometry = new THREE.TubeGeometry(carPath, 600, radius, 10, false);

//Set a different color on each face
for (var i = 0, j = geometry.faces.length; i < j; i++) {
  geometry.faces[i].color = new THREE.Color(
    "hsl(" + Math.floor(Math.random() * 290) + ",50%,50%)"
  );
}

var material = new THREE.MeshBasicMaterial({
  side: THREE.BackSide,
  vertexColors: THREE.FaceColors,
  side: THREE.DoubleSide,
  transparent: true,
  opacity: 1
});
var tube = new THREE.Mesh(geometry, material);
scene.add(tube);

//===================================================== add lighta
var light = new THREE.DirectionalLight(0xefefff, 1.25);
light.position.set(1, 1, 1).normalize();
scene.add(light);
var light = new THREE.DirectionalLight(0xffefef, 1.25);
light.position.set(-1, -1, -1).normalize();
scene.add(light);

//Create a point light in our scene. Makes everything gloomy
var light = new THREE.PointLight(0xffffff, 1, 100);
scene.add(light);

//===================================================== particles
var mergedGeometry = new THREE.Geometry();

var boxGeometry = new THREE.TetrahedronGeometry(0.25, 0);

var material = new THREE.MeshNormalMaterial({
  color: new THREE.Color("white")
});

for (var i = 0; i < 1000; i++) {
  var x = Math.random() * 125 - 75;
  var y = Math.random() * 125 - 75;
  var z = Math.random() * 125 - 75;

  boxGeometry.translate(x, y, z);

  mergedGeometry.merge(boxGeometry);

  boxGeometry.translate(-x, -y, -z);
}

var cubes = new THREE.Mesh(mergedGeometry, material);
scene.add(cubes);

//===================================================== Loader
//3d model from
var loader = new THREE.GLTFLoader();
var model;
loader.load(
  "sky-island.glb",
  function(gltf) {
    gltf.scene.traverse(function(node) {
      if (node instanceof THREE.Mesh) {
        node.castShadow = true;
        node.material.side = THREE.DoubleSide;
      }
    });

    model = gltf.scene;
    model.scale.set(3, 3, 3);
    model.position.set(0, -20, -10);
    //model.rotateY(Math.PI);
    scene.add(model);
  }
);

//===================================================== Loader
//3d model from https://3dwarehouse.sketchup.com/user/0438052632930067253040161/wingedkoopa67?nav=models
var clock = new THREE.Clock();
var mixer = null;
var firstObject;
var loader = new THREE.GLTFLoader();
loader.load(
  "sonic.glb",
  function(gltf) {
    gltf.scene.traverse(function(node) {
      if (node instanceof THREE.Mesh) {
        node.castShadow = true;
        node.material.side = THREE.DoubleSide;
      }
    });

    firstObject = gltf.scene;
    firstObject.scale.set(0.65, 0.65, 0.65);
    group.add(firstObject);

    console.log(gltf.animations); //shows all animations imported into the dopesheet in blender

    mixer = new THREE.AnimationMixer(firstObject);
    mixer.clipAction(gltf.animations[0]).play();

    document.body.addEventListener("click", jump);
    function jump() {
      mixer.clipAction(gltf.animations[0]).stop();
      mixer.clipAction(gltf.animations[1]).play();
      setTimeout(function() {
        mixer.clipAction(gltf.animations[1]).stop();
        mixer.clipAction(gltf.animations[0]).play();
      }, 900);
    }
  }
);

//===================================================== add model
var size = 0.05;
var meshList = [];

for (var i = 0; i < carPath.points.length; i++) {
  var x = carPath.points[i].x;
  var y = carPath.points[i].y;
  var z = carPath.points[i].z;

  var geometry = new THREE.TorusGeometry(3, 0.5, 8, 50);
  var material = new THREE.MeshBasicMaterial({
    color: new THREE.Color("yellow")
  });
  var secondObject = new THREE.Mesh(geometry, material);
  secondObject.position.set(x, y + 0.75, z);
  secondObject.scale.set(size, size, size);
  meshList.push(secondObject);
  scene.add(secondObject);
}

//===================================================== Collision Detection
function PlaySound() {
  bflat.play();
}

//calculate distance of the main object
firstBB = new THREE.Box3().setFromObject(group);

//calculate distance for all other objects
for (var i = 0; i < meshList.length; i++) {
  secondBB = new THREE.Box3().setFromObject(meshList[i]);
}

var count = 0;
function hit() {
  //recalculate distance for the main object
  firstBB = new THREE.Box3().setFromObject(group);
  //recalcuate distance for all the other objects
  for (var i = 0; i < meshList.length; i++) {
    secondBB = new THREE.Box3().setFromObject(meshList[i]);

    if (firstBB.isIntersectionBox(secondBB)) {
      PlaySound();
      info.style.color = "hsl(" + Math.floor(Math.random() * 290) + ",50%,50%)";
      info.innerHTML =
        Math.random() > 0.25
          ? "Superb!"
          : Math.random() > 0.25 ? "Oustanding!" : "Awesome!";

      TweenLite.to(info, 0.75, {
        css: { fontSize: "50px", opacity: 1 },
        ease: Power4.easeOut,
        onComplete: function() {
          TweenLite.to(info, 0.75, {
            css: { fontSize: "14px", opacity: 0 },
            ease: Power4.easeOut
          }); //end tween
        } //end onComplete
      }); //end tween
    } //end if
  } //end for
} //end hit

//===================================================== bloom
var renderScene = new THREE.RenderPass(scene, camera);
var shaderActive = "none";
var gui = new dat.GUI();
dat.GUI.toggleHide();
var composer;

var parameters = {
  x: 0,
  y: 30,
  z: 0,
  bloomStrength: 1.0,
  bloomRadius: 1.0,
  bloomThreshold: 0.45,
  useShaderNone: function() {
    setupShaderNone();
  },
  useShaderBloom: function() {
    setupShaderBloom();
  }
};

gui.add(parameters, "useShaderNone").name("Display Original Scene");

var folderBloom = gui.addFolder("Bloom");
var bloomStrengthGUI = folderBloom
  .add(parameters, "bloomStrength")
  .min(0.0)
  .max(2.0)
  .step(0.01)
  .name("Strength")
  .listen();
bloomStrengthGUI.onChange(function(value) {
  setupShaderBloom();
});
var bloomRadiusGUI = folderBloom
  .add(parameters, "bloomRadius")
  .min(0.0)
  .max(5.0)
  .step(0.01)
  .name("Radius")
  .listen();
bloomRadiusGUI.onChange(function(value) {
  setupShaderBloom();
});
var bloomThresholdGUI = folderBloom
  .add(parameters, "bloomThreshold")
  .min(0)
  .max(0.99)
  .step(0.01)
  .name("Threshold")
  .listen();
bloomThresholdGUI.onChange(function(value) {
  setupShaderBloom();
});
folderBloom.add(parameters, "useShaderBloom").name("Use Bloom Shader");
folderBloom.open();

//===================================================== functions

function setupShaderNone() {
  shaderActive = "none";
}

function setupShaderBloom() {
  composer = new THREE.EffectComposer(renderer);
  composer.addPass(new THREE.RenderPass(scene, camera));

  /*unreal bloom*/
  var effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
  effectFXAA.uniforms["resolution"].value.set(
    1 / window.innerWidth,
    1 / window.innerHeight
  );

  var copyShader = new THREE.ShaderPass(THREE.CopyShader);
  copyShader.renderToScreen = true;

  var bloomPass = new THREE.UnrealBloomPass(
    new THREE.Vector2(window.innerWidth, window.innerHeight),
    parameters.bloomStrength,
    parameters.bloomRadius,
    parameters.bloomThreshold
  );

  composer = new THREE.EffectComposer(renderer);
  composer.setSize(window.innerWidth, window.innerHeight);
  composer.addPass(renderScene);
  composer.addPass(effectFXAA);
  composer.addPass(bloomPass);
  composer.addPass(copyShader);
  shaderActive = "bloom";
}

function isShaderActive() {
  if (shaderActive == "none") {
    renderer.render(scene, camera);
  } else {
    composer.render();
  }
}

//active bloom on load
setupShaderBloom();

//===================================================== Animate
var percentage = 0;
var prevTime = Date.now();
function POV() {
  percentage += 0.00045;
  var p1 = carPath.getPointAt(percentage % 1);
  var p2 = carPath.getPointAt((percentage + 0.01) % 1);
  var p3 = carPath.getPointAt((percentage + 0.01 / 2) % 1);
  var p4 = carPath.getPointAt((percentage + 0.01 / 4) % 1);

  camera.lookAt(p2);
 

  group.position.set(p3.x, p3.y + 0.25, p3.z);
  group.lookAt(p2);
  camera.position.x = p4.x + 2;
  camera.position.y = p4.y + 1;
  camera.position.z = p4.z + 2;
  camera.lookAt(group.position);
}

function animate() {
  POV();
  hit();
  var delta = clock.getDelta();
  if (mixer != null) mixer.update(delta);

  //VR
  if (VR) {
    effect.render(scene, camera);
  } else {
    //renderer.render( scene, camera );
    isShaderActive();
  }

  requestAnimationFrame(animate);
  controls.update();
}
animate();

//set camera position
camera.position.x = 40;
camera.position.y = 50;
camera.position.z = 50;

更多學習內容:請關註我的知乎專欄    高級前端工程師前端學習教程,從基礎到進階,看完保證讓你的薪資上升一個臺階,你也能成為阿裡人(持續更新)

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. SELinux 背景知識 1.1 DAC 與 MAC 在 SELinux 出現之前,Linux 上的安全模型叫 DAC,全稱是 Discretionary Access Control,翻譯為自主訪問控制。 DAC 的核心思想很簡單,就是:進程理論上所擁有的許可權與執行它的用戶的許可權相同。比如, ...
  • 一、Swich語句 1.switch語句格式 swich(條件表達式){ case 表達式: 語句1; break; case 表示式: 語句2; break; case 表達式: 語句3: break; default: 語句n+1; break; } 註意點: (1)JS中判斷是否相等時是全等於 ...
  • 寫在前面 書籍介紹:《React進階之路》詳細介紹了React技術棧涉及的主要技術。本書分為基礎篇、進階篇和實戰篇三部分。基礎篇主要介紹React的基本用法,包括React 16的新特性;進階篇深入講解組件state、虛擬DOM、高階組件等React中的重要概念,同時對初學者容易困惑的知識點做了介紹 ...
  • 【目錄】 一、jQuery 簡介 二、jQuery 的基本使用 一、jQuery 簡介 1、介紹 jQuery內部封裝了原生的js代碼(還額外添加了很多的功能)能夠讓你通過書寫更少的代碼 完成js操作 類似於python裡面的模塊 在前端模塊不叫模塊 叫 “類庫” 相容多個瀏覽器的 你在使用jQue ...
  • 如何學好Web前端開發技術?前端學習路線是什麼?如今,移動開發的發展依舊如火如荼,企業對於Web前端人才需求產生了巨大的缺口,從事Web前端開發的程式員們則是其中較大的獲益者。Web前端的廣泛運用,造就了本身的優勢。現在學習Web前端是絕佳時期,抓住機會,拿高薪進名企就不再是問題。那麼想學好這門技術 ...
  • # 什麼是Hexo? Hexo 是一個快速、簡潔且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在幾秒內,即可利用靚麗的主題生成靜態網頁。 # 優勢 1. Node.js 所帶來的超快生成速度,讓上百個頁面在幾秒內瞬間完成渲染。 2. Hexo 支持 GitHub Fl... ...
  • function isInArray(arr, val) { let testStr = ',' + arr.join(",") + ","; return testStr.indexOf("," + val + ",") -1 } ...
  • let markPointData = op.series[params.seriesIndex].markPoint.data; let newMarkPointData = markPointData.filter(({ name }) => name !== params.name); d1 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...