第一节中讲解了openApi的调用,这一篇讲一下如何实现一个灯的控制。就用微信提供的lamp例子来做,将代码扒下来(实在是没办法,没有示例),整合到自己的项目中。lamp源码:http://files.cnblogs.com/files/stoneniqiu/lamp.zip

你可以自己扒,带参数的页面在浏览器中打开会马上跳转,不带参数的会提示参数不全,需要用mobile模式观看。

呈现的界面如下:

目录结构

解压开lamp.js ,目录如下,这个demo是基于sea.js+zepto实现,sea.js用来加载模块,zepto提供ajax请求和tab事件等。

common中包含了一个keyConfig.js(地址参数),一个reqData.js(请求封装)还有一个zepto,ui里是一个上面图片的中的slider一样的组件。util中是一组方法集合。最重要的就是lamp.js 。

  1. define(function (require) {
  2. var $ = require("common/zepto");
  3. var keyConfig = require("common/keyConfig");
  4. var reqData = require("common/reqData");
  5. var util = require("util/util");
  6. var ProcessBar = require("ui/process-bar");
  7.  
  8. var pageParam = {
  9. device_id: util.getQuery("device_id"),
  10. device_type: util.getQuery("device_type"),
  11. appid: util.getQuery("appid")
  12. };
  13. var lastModTime = 0;
  14.  
  15. var powerBtn = $("#powerBtn"), // 开关按钮
  16. lightBar;
  17. var device_status= {
  18. services: {
  19. lightbulb: {alpha:0},
  20. operation_status:{status:0}
  21. }
  22. }; // 数据对象
  23.  
  24. (function () {
  25. if(!pageParam.device_id || !pageParam.device_type){
  26. alert("页面缺少参数");
  27. return;
  28. }
  29. log("appid:" + pageParam.appid);
  30. log("device_id:" + pageParam.device_id);
  31. log("device_type:" + pageParam.device_type);
  32. powerBtn.on("tap", togglePower); // 开关按钮事件
  33. initBar();
  34. initInterval();
  35.  
  36. // todo : for test, delete before submit
  37. // renderPage({});
  38. })();
  39.  
  40. /**
  41. * 初始化进度条
  42. */
  43. function initBar() {
  44. log("初始化lightBar");
  45. lightBar = new ProcessBar({
  46. $id: "lightBar",
  47. min: 0,
  48. stepCount: 100,
  49. step: 1,
  50. touchEnd: function (val) {
  51. device_status.services.lightbulb.alpha = val;
  52. log("亮度值为:"+val);
  53. setData();
  54. }
  55. });
  56. }
  57. /**
  58. * 请求数据
  59. */
  60. function getData() {
  61. reqData.ajaxReq({
  62. //url: keyConfig.GET_LAMP_STATUS,
  63. url:'https://api.weixin.qq.com/device/getlampstatus',
  64. data: pageParam,
  65. onSuccess: renderPage,
  66. onError:function(msg) {
  67. log("获取数据失败:" + JSON.stringify(msg));
  68. }
  69. });
  70. }
  71. /**
  72. * 设置数据
  73. */
  74. function setData() {
  75. console.log("setUrl", keyConfig.SET_LAMP_STATUS);
  76. lastModTime = new Date().getTime(); // 更新最后一次操作时间
  77. reqData.ajaxReq({
  78. // url: keyConfig.SET_LAMP_STATUS,
  79. url: 'https://api.weixin.qq.com/device/setlampstatus',
  80. type: "POST",
  81. data: JSON.stringify(device_status)
  82. });
  83. log("setData:" + JSON.stringify(device_status));
  84.  
  85. }
  86.  
  87. /**
  88. * 开关按钮事件
  89. */
  90. function togglePower() {
  91. $("#switchBtn").toggleClass("on").toggleClass("off");
  92. log("灯的状态status:"+device_status.services.operation_status.status);
  93. if(device_status.services.operation_status.status==0){
  94. device_status.services.operation_status.status = 1;
  95. log("灯的状态:1");
  96.  
  97. } else {
  98. device_status.services.operation_status.status = 0;
  99. log("灯的状态:0");
  100. }
  101. setData();
  102. }
  103.  
  104. /**
  105. * 轮询
  106. */
  107. function initInterval() {
  108. getData();
  109. setInterval(function () {
  110. if((new Date().getTime() - lastModTime) > 2000){ // 当有设置操作时,停止1s轮询,2秒后继续轮询
  111. getData();
  112. }
  113. }, 1000);
  114. }
  115.  
  116. /**
  117. * 渲染页面
  118. */
  119. function renderPage(json) {
  120. // todo : for test, delete before submit
  121. // json = {
  122. // device_status: {
  123. // services: {
  124. // operation_status: {
  125. // status: 0
  126. // },
  127. // lightbulb: {
  128. // alpha: 0
  129. // }
  130. // }
  131. // }
  132. // };
  133. log("renderPage:"+json);
  134. if(!json.device_status){
  135. return;
  136. }
  137. console.log("json", json);
  138. device_status = json.device_status;
  139. log(device_status);
  140. if(device_status.services.operation_status.status==0){
  141. $("#switchBtn").addClass("on").removeClass("off");
  142. } else {
  143. $("#switchBtn").addClass("off").removeClass("on");
  144. }
  145. lightBar.setVal(device_status.services.lightbulb.alpha);
  146. }
  147. });/* |xGv00|4199711a9ade00e2807e7ea576d92f55 */

首先我们看到pageParam对象是获取页面上参数的,device_id,device_type以及appid三个参数。其实有用的只有前面两个,因为appid的话,后台服务器已经配置了,而且在微信中的通过“进入面板”的时候只附带了id和type两个参数。然后device_status是一个设备状态对象对象是灯,根据微信services的定义,灯有一个亮度值。这个在上一篇提到过。然后是一个立即执行的匿名函数,这个函数函数里面会先检查一下参数,然后初始化开关和亮度条。最好进入循环。initInterval中就是不断的通过getdata获取数据。注意到这儿有一个lastModTime的比较,然后延时2秒再触发,这个地方主要是因为每次设置之后再从服务器捞到数据有一个延时。原本是10,你设置了20,bar也到了20的位置,但是呢,服务器还有一个10在路上发过来,你设置的20并没有马上失效,这会有一个卡顿的效果。但这个两秒也不是那么的有效,卡顿还是会有;另外一方面就是,不能设置太快,设置太快了会报50019的错误(设备正在被操作);getdata成功后,就是renderpage,这个不用解释了。注意到在绑定开关时间的地方,其实是先调用了一次setdata

  1. powerBtn.on("tap", togglePower);
  2.  
  3. function togglePower() {
  4. $("#switchBtn").toggleClass("on").toggleClass("off");
  5. log("灯的状态status:"+device_status.services.operation_status.status);
  6. if(device_status.services.operation_status.status==0){
  7. device_status.services.operation_status.status = 1;
  8. log("灯的状态:1");
  9.  
  10. } else {
  11. device_status.services.operation_status.status = 0;
  12. log("灯的状态:0");
  13. }
  14. setData();
  15. }

这个作用有两个,一个是获取设备目前的状态,因为设备可能没有开启,或者没有联网,二个是将参数传递给后台,不然getdata无效。最后理清一下思路就是

获取参数-->初始化-->setdata一次-->循环-->渲染页面  界面操作-->setdata-->延时读取。 加上后端的部分,全部的流程图如下。

所以拿到前端代码只是一半,后端还需要自己实现。

实现

纯静态文件是无法请求微信服务器的,所以我们需要自己实现后台的部分,这也是第一节中要讲的目的。

html:

  1. @{
  2. Layout = null;
  3. }
  4.  
  5. <!DOCTYPE html>
  6.  
  7. <html>
  8. <head>
  9. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  10. <meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  11. <title>我的灯泡</title>
  12. <link href="/css/common.css" rel="stylesheet" />
  13. <link href="/css/light_switch.css" rel="stylesheet" />
  14. </head>
  15.  
  16. <body>
  17. <div>
  18. <div class="body">
  19. <div class="inner">
  20. <div id="switchBtn" class="status_button off">
  21. <div class="button_wrp">
  22. <div class="button_mask">
  23. <div class="alerter_button" id="powerBtn">
  24. <i class="status_pot"></i>
  25. <span class="on">ON</span>
  26. <span class="off">OFF</span>
  27. </div>
  28. </div>
  29. </div>
  30. <div class="on">
  31. <h2>灯已开</h2>
  32. </div>
  33. </div>
  34. <div id="reData"></div>
  35. </div>
  36. </div>
  37. <div class="foot">
  38. <div class="slider_box J_slider_box">
  39. <i class="slider_box_icon icon dark"></i>
  40. <div id="lightBar" class="slider_box_bar">
  41. <div class="slider_box_slider J_slider" style="left:0%">
  42. <p class="slider_box_slider_label J_value"></p>
  43. <i class="slider_box_slider_touch"></i>
  44. </div>
  45. <div class="slider_box_line">
  46. <span class="slider_box_line_fill J_fill" style="width:0%"></span>
  47. </div>
  48. </div>
  49. <i class="slider_box_icon icon light"></i>
  50. </div>
  51. </div>
  52. </div>
  53. <script src="/js/sea.js"></script>
  54. <script>
  55. seajs.config({
  56. base: '/js/',
  57. //map: [[/^(.*\.(?:css|js))(.*)$/i, "$1"]],
  58. charset: 'utf-8'
  59. });
  60. seajs.use("baby");
  61. </script>
  62. </body>
  63. </html>

自己的实现就拿掉了遮罩和config部分,将sea.js的目录改到自己对应的目录即可:

  1. seajs.config({
  2. base: '/js/',
  3. //map: [[/^(.*\.(?:css|js))(.*)$/i, "$1"]],
  4. charset: 'utf-8'
  5. });
  6. seajs.use("baby");

这个baby(命名和产品有关~)就相当于是lamp。 另外就是,修改请求地址。也就是通过后台调用api来实现getdate和setdata。第一版我修改的js和lamp.js的差别不大 就增加了一个log为了调试,修改调用路径。

  1. define(function (require) {
  2. var $ = require("common/zepto");
  3. var util = require("util/util");
  4. var ProcessBar = require("ui/process-bar");
  5.  
  6. var requestData = {
  7. services: {
  8. lightbulb: { alpha: 10 },
  9. air_conditioner: {},
  10. power_switch: {},
  11. operation_status: { status: 0 }
  12. },
  13. device_type: util.getQuery("device_type"),
  14. device_id: util.getQuery("device_id"),
  15. user: '',
  16. };
  17. var lastModTime = 0;
  18. var powerBtn = $("#powerBtn"), // 开关按钮
  19. lightBar;
  20.  
  21. function log(msg, arg) {
  22. console.log(msg, arg);
  23. msg = JSON.stringify(msg);
  24. if (arg) {
  25. msg = msg + "," + JSON.stringify(arg);
  26. }
  27. $.post('/device/log', { msg: msg });
  28. }
  29. (function () {
  30. bindEvent();
  31. if (!requestData.device_id || !requestData.device_type) {
  32. alert("页面缺少参数");
  33. return;
  34. }
  35. powerBtn.on("tap", togglePower); // 开关按钮事件
  36. initBar();
  37. queryDevice();
  38. })();
  39.  
  40. function bindEvent() {
  41. $(".footer .nav_side li").click(function () {
  42. activePage($(this).data("index"), $(this));
  43. });
  44. }
  45. function activePage(index, $self) {
  46. $self.parent('li').addClass("on");
  47. $body.find('.page:eq(' + index + ')').addClass("active").siblings().removeClass("active");
  48. }
  49. /**
  50. * 初始化进度条
  51. */
  52. function initBar() {
  53. log("初始化lightBar");
  54. lightBar = new ProcessBar({
  55. $id: "lightBar",
  56. min: 0,
  57. stepCount: 100,
  58. step: 1,
  59. touchEnd: function (val) {
  60. requestData.services.lightbulb.alpha = val;
  61. log("亮度值为:" + val);
  62. setData();
  63. }
  64. });
  65. }
  66.  
  67. /**
  68. * 开关按钮事件
  69. */
  70. function togglePower() {
  71. $("#switchBtn").toggleClass("on").toggleClass("off");
  72. if (requestData.services.operation_status.status == 0) {
  73. requestData.services.operation_status.status = 1;
  74. log("灯的状态:1");
  75.  
  76. } else {
  77. requestData.services.operation_status.status = 0;
  78. log("灯的状态:0");
  79. }
  80. setData();
  81. }
  82. function queryDevice() {
  83. $.getJSON('/device/RequestDeviceStatus', { reqstr: JSON.stringify(requestData) },
  84. function (data) {
  85. console.log(data);
  86. if (data.error_code == 0) {
  87. //请求成功;
  88. initInterval();
  89. console.log("查询成功");
  90. } else {
  91. alert(data.error_msg);
  92. }
  93. });
  94. }
  95.  
  96. /**
  97. * 轮询
  98. */
  99. function initInterval() {
  100. getData();
  101. setInterval(function () {
  102. if ((new Date().getTime() - lastModTime) > 2000) { // 当有设置操作时,停止1s轮询,2秒后继续轮询
  103. getData();
  104. }
  105. }, 1000);
  106. }
  107.  
  108. function setData() {
  109. $.getJSON('/device/RequestDeviceStatus', { reqstr: JSON.stringify(requestData) }, function (data) {
  110. console.log(data);
  111. lastModTime = new Date().getTime();
  112. if (data.error_code == 0) {
  113. console.log("设置成功");
  114. }
  115. });
  116. }
  117.  
  118. function getData() {
  119. $.post('/device/getData', function (data) {
  120. $("#reData").html(JSON.stringify(data));
  121. if (data && data.services) {
  122. renderPage(data);
  123. }
  124. });
  125.  
  126. };
  127. function renderPage(json) {
  128. if (!json.services) {
  129. return;
  130. }
  131. console.log("json", json);
  132. requestData = json;
  133. if (requestData.services.operation_status.status == 0) {
  134. $("#switchBtn").addClass("off").removeClass("on");
  135. } else {
  136. $("#switchBtn").addClass("on").removeClass("off");
  137. }
  138. lightBar.setVal(requestData.services.lightbulb.alpha);
  139. }
  140. })

我将pageParam和device_status做成了一个对象。requestData。

  1. var requestData = {
  2. services: {
  3. lightbulb: { alpha: 10 },
  4. // air_conditioner: {},
  5. power_switch: {},
  6. operation_status: { status: 0 }
  7. },
  8. device_type: util.getQuery("device_type"),
  9. device_id: util.getQuery("device_id"),
  10. user: '',
  11. };

后台就是两个主要方法,一个设置(查询页就是设置),一个读取。这里又回到上一节的内容了。我先查询一次设备(lamp中在绑定)之后,再进入循环。

setdata

  1. public ActionResult RequestDeviceStatus(string reqstr)
  2. {
  3. if (string.IsNullOrEmpty(reqstr))
  4. {
  5. return Json("-1", JsonRequestBehavior.AllowGet);
  6. }
  7.  
  8. var args = JsonConvert.DeserializeObject<RequestData>(reqstr);
  9. args.user = getOpenId(args.device_type, args.device_id);
  10. Session["warmwood"] = args.device_id;
  11. //args.services.air_conditioner = null;
  12. args.services.power_switch = null;
  13. args.services.lightbulb.value_range = null;
  14. try
  15. {
  16. var res = wxDeviceService.RequestDeviceStatus(getToken(), args);
  17. if (res.error_code != )
  18. {
  19. Logger.Debug("error_code:" + res.error_code);
  20. Logger.Debug("error_msg:" + res.error_msg);
  21. }
  22. return Json(res, JsonRequestBehavior.AllowGet);
  23. }
  24. catch (ErrorJsonResultException e)
  25. {
  26. if (e.JsonResult.errcode.ToString() == "access_token expired")
  27. {
  28. //重新获取token
  29. }
  30. Logger.Debug("请求失败:" + e.Message);
  31. }
  32. return Json("-1", JsonRequestBehavior.AllowGet);
  33. }

这个方法先将字符串转成我们的RequestData对象,RequestData如下:

  1. public class RequestData
  2. {
  3. public string device_type { get; set; }
  4. public string device_id { get; set; }
  5. public string user { get; set; }
  6. public Service services { get; set; }
  7. public object data { get; set; }
  8. }

services就是根据微信services定义的,可以参考上一节,然后用wxDeviceService请求。

  1. var res = wxDeviceService.RequestDeviceStatus(getToken(), args);
  2. if (res.error_code != )
  3. {
  4. Logger.Debug("error_code:" + res.error_code);
  5. Logger.Debug("error_msg:" + res.error_msg);
  6. }
  7. return Json(res, JsonRequestBehavior.AllowGet);

设置之后马上会受到是否设置成功的响应,error_code 可能为50019(设置频繁),50013(网络问题)等等。真正的设备状态是通过getdata获得的。

getdata

  1. public JsonResult GetData()
  2. {
  3. var userdata = getUserWxData();
  4. return Json(userdata.ResponseData, JsonRequestBehavior.AllowGet);
  5. }

getdata比较简单就是返回数据,但是这个数据是在ReceiveWXMsg方法中设置的。这个上一节也讲过,这是在公众号后台我们设置的一个地址。

  1. public string ReceiveWXMsg()
  2. {
  3. //somecode
  4. try
  5. {
  6. var userdata = getUserWxData();
  7. var data = wxDeviceService.GetDeviceStatus(Request);
  8. userdata.ResponseData = data;
  9. Logger.Debug("ResponseData.asy_error_code:" + userdata.ResponseData.asy_error_code);
  10. Logger.Debug("ResponseData.asy_error_msg:" + userdata.ResponseData.asy_error_msg);
  11. setUserWxData(userdata);
  12. }
  13. catch (Exception e)
  14. {
  15. Logger.Debug(e.Message);
  16. }
  17.  
  18. return echostr;
  19. }

wxDeviceService如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net.Http;
  7. using System.Web;
  8. using Newtonsoft.Json;
  9. using Niqiu.Core.Domain.Common;
  10. using Senparc.Weixin;
  11. using Senparc.Weixin.Exceptions;
  12. using SendHelp= Senparc.Weixin.CommonAPIs.CommonJsonSend;
  13.  
  14. namespace Portal.MVC.WXDevice
  15. {
  16. public class WxDeviceService:IWxDeviceService
  17. {
  18. //private readonly ICacheManager _cacheManager;
  19. //public WxDeviceService(ICacheManager cacheManager)
  20. //{
  21. // _cacheManager = cacheManager;
  22. //}
  23.  
  24. public TokenResult GetAccessToken()
  25. {
  26. var url = string.Format(WxDeviceConfig.AccessTokenUrl, WxDeviceConfig.AppId, WxDeviceConfig.APPSECRET);
  27. var res = SendHelp.Send<TokenResult>(null, url, null, CommonJsonSendType.GET);
  28. return res;
  29. }
  30.  
  31. public WxResponseData GetDeviceStatus(HttpRequestBase request)
  32. {
  33. Stream postData = request.InputStream;
  34. StreamReader sRead = new StreamReader(postData);
  35. string postContent = sRead.ReadToEnd();
  36. if (!string.IsNullOrEmpty(postContent))
  37. {
  38. Logger.Debug("收到数据:" + postContent);
  39. }
  40. try
  41. {
  42. var data = JsonConvert.DeserializeObject<WxResponseData>(postContent);
  43. data.rawStr = postContent;
  44. Logger.Debug("转换消息状态:" + data.asy_error_msg);
  45. return data;
  46. }
  47. catch (Exception e)
  48. {
  49. Logger.Debug(e.Message);
  50. throw;
  51. }
  52. }
  53.  
  54. public OpenApiResult RequestDeviceStatus(string accessToken, RequestData data)
  55. {
  56. var url = string.Format(WxDeviceConfig.GetDeviceStatusUrl, accessToken);
  57. return SendHelp.Send<OpenApiResult>(accessToken, url, data);
  58. }
  59.  
  60. public OpenApiResult SetDevice(string accessToken, RequestData data)
  61. {
  62. var url = string.Format(WxDeviceConfig.GetDeviceStatusUrl, accessToken);
  63. return SendHelp.Send<OpenApiResult>(accessToken, url, data);
  64. }
  65.  
  66. public string GetOpenId(string accessToken,string deviceType,string deviceId)
  67. {
  68. try
  69. {
  70. var url = string.Format(WxDeviceConfig.GetOpenid, accessToken, deviceType, deviceId);
  71. var res = SendHelp.Send<OpenIdResult>(accessToken, url, null, CommonJsonSendType.GET);
  72. return res.GetOpenId();
  73. }
  74. catch (ErrorJsonResultException e)
  75. {
  76. Logger.Debug(e.Message);
  77. throw;
  78. }
  79. }
  80. }
  81. }

这方法读到数据后就交给了userdata 缓存起来。在getdata方法中返回。

  1. private UserWxData getUserWxData()
  2. {
  3. var target = _cacheManager.Get<UserWxData>(userKey) ?? new UserWxData();
  4. return target;
  5. }
  6.  
  7. private string userKey
  8. {
  9. get
  10. {
  11. var key = Session["warmwood"] ?? Session.SessionID;
  12. Session.Timeout = ;
  13. return key.ToString();
  14. }
  15. }

UserWxData是我自定义的对象,包含了下面的几个熟悉。

  1. public class UserWxData
  2. {
  3. private WxResponseData _responseData;
  4.  
  5. public UserWxData()
  6. {
  7. CreateTime = DateTime.Now;
  8. }
  9. public DateTime CreateTime { get; set; }
  10. public TokenResult AccessToken { get; set; }
  11.  
  12. public WxResponseData ResponseData
  13. {
  14. get { return _responseData??(_responseData=new WxResponseData()); }
  15. set { _responseData = value; }
  16. }
  17.  
  18. public string OpenId { get; set; }
  19. }

比较重要的是token和responseData。WxResponseData 也就是最终要发给页面上的对象。包含你需要的功能的参数。

  1. public class WxResponseData
  2. {
  3. public int asy_error_code { get; set; }
  4.  
  5. public string asy_error_msg { get; set; }
  6.  
  7. public string create_time { get; set; }
  8.  
  9. public string msg_id { get; set; }
  10.  
  11. /// <summary>
  12. /// notify 说明是设备变更
  13. /// set_resp 说明是设置设备
  14. /// get_resp 说明获取设备信息
  15. /// </summary>
  16. public string msg_type { get; set; }
  17.  
  18. public string device_type { get; set; }
  19. public string device_id { get; set; }
  20. public object data { get; set; }
  21.  
  22. public Service services { get; set; }
  23.  
  24. public string user { get; set; }
  25.  
  26. public string rawStr { get; set; }
  27. }

severices看自己的设备定义,比如我现在包含了空调,开关,温度湿度。

  1. public class Service
  2. {
  3. public lightbulb lightbulb { get; set; }
  4.  
  5. public air_conditioner air_conditioner { get; set; }
  6.  
  7. public power_switch power_switch { get; set; }
  8.  
  9. public operation_status operation_status { get; set; }
  10.  
  11. public tempe_humidity tempe_humidity { get; set; }
  12. }

到这儿,整个过程就讲完了,获取token和openid上一节讲过,就不赘述了。如果后端是node的话,就不需要这么多的类型转换了。

最后可以看下效果:

小结:以上就这一篇的全部内容,流程图画可能画的不够好,但这都是摸索出来的结果,微信硬件虽然提供了自定义的链接设置,但是没有提供demo。文中有不对或者不合适的地方欢迎拍砖,欢迎加Q交流。

微信硬件H5面板开发(二) ---- 实现一个灯的控制的更多相关文章

  1. 微信硬件H5面板开发(一) ---- 调用openApi

    微信硬件平台是微信推出连接物与人,物与物的IOT解决方案.也就是说可以通过微信控制各种智能设备.比如一些蓝牙设备.空调.电视等等. 我本身不懂硬件(虽然是电子信息专业),硬件是北航的两个研究生在弄,小 ...

  2. 移动端H5页面开发,碰到一个字体变大的BUG

    移动端H5页面开发,碰到一个字体变大的BUG webkit内核下,对不定高宽的元素可能会放大其字体.那么,就可以设置一个max-width:或者使用-webkit-text-size-adjust: ...

  3. 能挣钱的微信JSSDK+H5混合开发

    H5喊了那么久,有些人都说不实用,有些人却利用在微信中开发H5应用赚得盆满钵满.微信JSSDK + HTML 5,让移动Web开发与微信结合轻而易举!跨平台.零成本,让大众创业变得更方便. 我觉得现在 ...

  4. UnrealEngine4入门(二) 实现一个可用按键控制的球体

    源码摘自官网guide,加上部分自己的理解和注释 接上篇博客 本文实现一个可用WASD控制滚动的球体 先创建一个可见的球体: 在CollidingPawn.cpp的构造函数ACollidingPawn ...

  5. H5混合开发二维码扫描以及调用本地摄像头

    今天主管给了我个需求,说要用混合开发,用H5调用本地摄像头进行扫描二维码,我之前有做过原生安卓的二维码扫一扫,主要是通过调用zxing插件进行操作的,其中还弄了个闪光灯.但是纯H5的没接触过,心里没底 ...

  6. 微信公众号平台开发(二)信息的分类.md

    在上一篇博客中,我们只是简单地与微信服务器建立了连接,接下来就是从微信服务器中接收信息了.在SecurityController中,我定义了两个方法(get和post).Get方法是我们用来与微信服务 ...

  7. Java微信公众平台开发之公众号支付(微信内H5调起支付)

    官方文档点击查看准备工作:已通过微信认证的公众号,必须通过ICP备案域名(否则会报支付失败)借鉴了很多大神的文章,在此先谢过了 整个支付流程,看懂就很好写了 一.设置支付目录 在微信公众平台设置您的公 ...

  8. C#开发微信门户及应用(44)--微信H5页面开发的经验总结

    在我们开发微信页面的时候,需要大量用到了各种呈现的效果,一般可以使用Boostrap的效果来设计不同的页面,不过微信团队也提供很多这方面的资源,包括JSSDK的接口,以及Weui的页面样式和相关功能页 ...

  9. C#微信公众号接口开发,灵活利用网页授权、带参数二维码、模板消息,提升用户体验之完成用户绑定个人微信及验证码获取

    一.前言 当下微信公众号几乎已经是每个公司必备的,但是大部分微信公众账号用户体验都欠佳,特别是涉及到用户绑定等,需要用户进行复杂的操作才可以和网站绑定,或者很多公司直接不绑定,而是每次都让用户填写账号 ...

随机推荐

  1. C语言 · 薪水计算

    问题描述 编写一个程序,计算员工的周薪.薪水的计算是以小时为单位,如果在一周的时间内,员工工作的时间不超过40 个小时,那么他/她的总收入等于工作时间乘以每小时的薪水.如果员工工作的时间在40 到50 ...

  2. Hyper-V2:向VM增加虚拟硬盘

    使用Hyper-V创建VM,在VM成功安装OS之后,发现VM只有一个逻辑盘C,用于存储VM的操作系统.在产品环境中,需要向VM增加虚拟硬盘,便于将数据单独存储在不同的逻辑盘符中.在Hyper-V中,分 ...

  3. 探索ASP.NET MVC5系列之~~~6.Session篇(进程外Session)

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  4. HTML kbd键盘元素

    1. 说明 kbd :即Keyboard Input Element(键盘输入元素).表示键盘按键的语义元素,常用于网页上对快捷键.按键说明的场景. 样式规格:内联样式. 为了在页面上突出显示,可以给 ...

  5. Android 获取系统相册中的所有图片

    Android 提供了API可获取到系统相册中的一些信息,主要还是通过ContentProvider 来获取想要的内容. 代码很简单,只要熟悉ContentProvider 就可以了. public ...

  6. tomcat开发远程调试端口以及利用eclipse进行远程调试

    一.tomcat开发远程调试端口 方法1 WIN系统 在catalina.bat里:  SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compi ...

  7. MAVEN学习-第一个Maven项目的构建

    MAVEN安装成功之后就可以进行项目的构建和管理了: 为什么要用maven进行项目的构建和管理? 对于初学者来说一个最直接的也是最容易里的优点在于JAR包的管理,相对于以前开发一个项目的时候我们需要用 ...

  8. 使用NUnit为游戏项目编写高质量单元测试的思考

    0x00 单元测试Pro & Con 最近尝试在我参与的游戏项目中引入TDD(测试驱动开发)的开发模式,因此单元测试便变得十分必要.这篇博客就来聊一聊这段时间的感悟和想法.由于游戏开发和传统软 ...

  9. 由js apply与call方法想到的js数据类型(原始类型和引用类型)

    原文地址:由js apply与call方法想到的js数据类型(原始类型和引用类型) js的call方法与apply方法的区别在于第二个参数的不同,他们都有2个参数,第一个为对象(即需要用对象a继承b, ...

  10. 2016/12/28_javascript

    今天学习的主要内容: javascript: 1.if语句,switch语句,while循环以及for循环: 1)if语句 if(boolean){}; if(boolean){} else if(b ...