博客 (5)

SignalR 是一个开源的实时通信库,用于构建实时 Web 应用程序。它提供了一个简单的 API,可以在客户端和服务器之间建立持久连接,以便实时地推送数据。

与传统的 WebSocket 相比,SignalR 提供了更高级的功能和更简单的开发体验。下面是一些主要区别:

  • 支持多种传输方式:SignalR 可以使用多种传输方式,包括 WebSocket、Server-Sent Events(SSE)、长轮询和 Forever Frame。这使得 SignalR 在不同的环境中都能提供实时通信的能力,即使某些浏览器不支持 WebSocket,也可以使用其他传输方式。

  • 自动处理连接管理:SignalR 管理连接的生命周期,包括连接的建立、断开和重新连接。它会自动处理连接的失败和重新连接的逻辑,简化了开发人员的工作。

  • 服务器端推送:SignalR 允许服务器端主动推送消息给客户端,而不需要客户端发起请求。这使得实时通信变得更加高效和实时,适用于聊天应用、实时监控等场景。

  • 跨平台支持:SignalR 可以在多个平台上使用,包括 .NET、Java、JavaScript 等。这使得开发人员可以使用自己熟悉的语言和框架来构建实时应用程序。


微软官方提供了针对 ASP.NET Core Web 应用(Razor 页面)的详细教程,这里给出 MVC 版本入门教程。


最终将创建一个正常运行的聊天应用:

signalr-get-started-finished.png


创建 Web 应用项目

image.png

image.png


添加 SignalR 客户端库

在“解决方案资源管理器”>中,右键单击项目,然后选择“添加”“客户端库”。

在“添加客户端库”对话框中:

  • “提供程序”选择“unpkg”

  • “库”,请输入 @microsoft/signalr@latest。

  • 选择“选择特定文件”,展开“dist/browser”文件夹,然后选择 signalr.js 和 signalr.min.js。

  • 点击“安装” 。

image.png


创建 SignalR Hubs 类

using Microsoft.AspNetCore.SignalR;

/// <summary>
/// Hub 类管理连接、组和消息
/// </summary>
public class ChatHub : Hub
{
        /// <summary>
        /// 可通过已连接客户端调用 SendMessage,以向所有客户端发送消息
        /// </summary>
    public async Task SendMessage(string user, string message)
    {
        // Clients.All 向所有的客户端发送消息
        // ReceiveMessage 是客户端监听的方法
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

其父类 Hub 可管理连接、组和消息。这里演示的是向所有客户端发送消息。


配置 SignalR

打开 Program.cs,添加注入:

builder.Services.AddSignalR();

添加路由:

app.MapHub<ChatHub>("/chatHub");


添加 SignalR 客户端代码

视图页面:

<div class="container">
    <div class="row p-1">
        <div class="col-1">用户</div>
        <div class="col-5"><input type="text" id="userInput" /></div>
    </div>
    <div class="row p-1">
        <div class="col-1">消息</div>
        <div class="col-5"><input type="text" class="w-100" id="messageInput" /></div>
    </div>
    <div class="row p-1">
        <div class="col-6 text-end">
            <input type="button" id="sendButton" value="发送消息" />
        </div>
    </div>
    <div class="row p-1">
        <div class="col-6">
            <hr />
        </div>
    </div>
    <div class="row p-1">
        <div class="col-6">
            <ul id="messagesList"></ul>
        </div>
    </div>
</div>
<script src="~/lib/microsoft/signalr/dist/browser/signalr.min.js"></script>
<script src="~/js/chat.js"></script>

chat.js 文件:

"use strict";

var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

// 在连接建立之前禁用发送按钮
document.getElementById("sendButton").disabled = true;

connection.on("ReceiveMessage", function (user, message) {
    var li = document.createElement("li");
    document.getElementById("messagesList").appendChild(li);
    // 修改此处时应注意脚本注入问题
    li.textContent = `${user} says ${message}`;
});

connection.start().then(function () {
    document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
    return console.error(err.toString());
});

document.getElementById("sendButton").addEventListener("click", function (event) {
    var user = document.getElementById("userInput").value;
    var message = document.getElementById("messageInput").value;
    connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString());
    });
    event.preventDefault();
});


完成。在线示例:https://xoyozo.net/Demo/SignalRDemo






xoyozo 1 年前
1,196

推荐使用 SignalR 来平替 WebSocket,参考教程


【服务端(.NET)】

一、创建 WebSocketHandler 类,用于处理客户端发送过来的消息,并分发到其它客户端

public class WebSocketHandler
{
    private static List<WebSocket> connectedClients = [];

    public static async Task Handle(WebSocket webSocket)
    {
        connectedClients.Add(webSocket);

        byte[] buffer = new byte[1024];
        WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        while (!result.CloseStatus.HasValue)
        {
            string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
            Console.WriteLine($"Received message: {message}");

            // 处理接收到的消息,例如广播给其他客户端
            await BroadcastMessage(message, webSocket);

            result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        }

        connectedClients.Remove(webSocket);
        await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
    }

    private static async Task BroadcastMessage(string message, WebSocket sender)
    {
        foreach (var client in connectedClients)
        {
            if (client != sender && client.State == WebSocketState.Open)
            {
                await client.SendAsync(Encoding.UTF8.GetBytes(message), WebSocketMessageType.Text, true, CancellationToken.None);
            }
        }
    }
}

二、在 Program.cs 或 Startup.cs 中插入:

// 添加 WebSocket 路由
app.UseWebSockets();
app.Use(async (context, next) =>
{
    if (context.Request.Path == "/chat")
    {
        if (context.WebSockets.IsWebSocketRequest)
        {
            WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
            await WebSocketHandler.Handle(webSocket);
        }
        else
        {
            context.Response.StatusCode = 400;
        }
    }
    else
    {
        await next();
    }
});

这里的路由路径可以更改,此处将会创建一个 WebSocket 连接,并将其传递给 WebSocketHandler.Handle 方法进行处理。

也可以用控制器来接收 WebSocket 消息。


【客户端(JS)】

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
    <title>WebSocket 示例(JS + ASP.NET)</title>
    <style>
        #list { width: 100%; max-width: 500px; }
        .tip { color: red; }
    </style>
</head>
<body>
    <h2>WebSocket 示例(JS + ASP.NET)</h2>
    <textarea id="list" readonly rows="20"></textarea>
    <div>
        <input type="text" id="msg" />
        <button onclick="fn_send()">发送</button>
        <button onclick="fn_disconnect()">断开</button>
    </div>
    <p id="tip_required">请输入要发送的内容</p>
    <p id="tip_closed">WebSocket 连接未建立</p>

    <script>
        var wsUrl = "wss://" + window.location.hostname + ":" + window.location.port + "/chat";
        var webSocket;
        var domList = document.getElementById('list');

        // 初始化 WebSocket 并连接
        function fn_ConnectWebSocket() {
            webSocket = new WebSocket(wsUrl);

            // 向服务端发送连接请求
            webSocket.onopen = function (event) {
                var content = domList.value;
                content += "[ WebSocket 连接已建立 ]" + '\r\n';
                domList.innerHTML = content;

                document.getElementById('tip_closed').style.display = 'none';

                webSocket.send(JSON.stringify({
                    msg: 'Hello'
                }));
            };

            // 接收服务端发送的消息
            webSocket.onmessage = function (event) {
                if (event.data) {
                    var content = domList.value;
                    content += event.data + '\r\n';
                    domList.innerHTML = content;
                    domList.scrollTop = domList.scrollHeight;
                }
            };

            // 各种情况导致的连接关闭或失败
            webSocket.onclose = function (event) {
                var content = domList.value;
                content += "[ WebSocket 连接已关闭,3 秒后自动重连 ]" + '\r\n';
                domList.innerHTML = content;

                document.getElementById('tip_closed').style.display = 'block';

                setTimeout(function () {
                    var content = domList.value;
                    content += "[ 正在重连... ]" + '\r\n';
                    domList.innerHTML = content;
                    fn_ConnectWebSocket();
                }, 3000); // 3 秒后重连
            };
        }

        // 检查连接状态
        function fn_IsWebSocketConnected() {
            if (webSocket.readyState === WebSocket.OPEN) {
                document.getElementById('tip_closed').style.display = 'none';
                return true;
            } else {
                document.getElementById('tip_closed').style.display = 'block';
                return false;
            }
        }

        // 发送内容
        function fn_send() {
            if (fn_IsWebSocketConnected()) {
                var message = document.getElementById('msg').value;
                document.getElementById('tip_required').style.display = message ? 'none' : 'block';
                if (message) {
                    webSocket.send(JSON.stringify({
                        msg: message
                    }));
                }
            }
        }

        // 断开连接
        function fn_disconnect() {
            if (fn_IsWebSocketConnected()) {
                // 部分浏览器调用 close() 方法关闭 WebSocket 时不支持传参
                // webSocket.close(001, "Reason");
                webSocket.close();
            }
        }

        // 执行连接
        fn_ConnectWebSocket();
    </script>
</body>
</html>


【在线示例】

https://xoyozo.net/Demo/JsNetWebSocket


【其它】

  • 服务端以 IIS 作为 Web 服务器,那么需要安装 WebSocket 协议

  • 一个连接对应一个客户端(即 JS 中的 WebSocket 对象),注意与会话 Session 的区别

  • 在实际项目中使用应考虑兼容性问题

  • 程序设计应避免 XSS、CSRF 等安全隐患

  • 参考文档:https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets

xoyozo 1 年前
794
  1. 购买 ECS

  2. 解析域名(非网站域名)挂载磁盘(若有另购)、修改实例名称、主机名

  3. 设置阿里云(重要)

  4. 远程连接进入 ECS(若解析未生效可以先用 IP)(若新服默认使用 3389 端口,可先在安全组临时放行 3389 端口)

  5. 开启 Windows 防火墙(使用推荐设置)

  6. Windows 更新、并在高级选项中开启(更新 Windows 时接收其它 Microsoft 产品的更新)

  7. 安装 IIS:服务器管理器-添加角色和功能-勾选“Web 服务器(IIS)”包括管理工具

    建议勾选:

    默认已勾选项

    按需安装 IP 和域限制

    常见 HTTP 功能(不建议安装 WebDAV 发布

    跟踪(即“失败请求跟踪”)

    请求监视器、日志记录工具、

    按需安装 ASP

    按需安装 ASP.NET 4.8(会同时勾选 .NET Extensibility 4.8、ISAPI 扩展、ISAPI 筛选器)

    按需安装 WebSocket 协议

    应用程序初始化(建议安装)

    管理服务(用于 Web 部署)

  8. 细节:设置任务栏;设置桌面图标;个性化-颜色-勾选“标题栏和窗口边框”;设置输入法;

  9. 更改远程桌面端口

  10. 按需安装:

    下载 URL 重写(文件名:rewrite_amd64_zh-CN.msi)

    下载 MySQL Connector/NET(文件名:mysql-connector-net-8.0.19.msi)

    下载 ASP.NET Core 运行时 Hosting Bundle(文件名:dotnet-hosting-*.*.*-win.exe)

    下载 .NET 桌面运行时 Windows x64(文件名:windowsdesktop-runtime-*.*.*-win-x64.exe)

    下载 Web Deploy(文件名:WebDeploy_amd64_zh-CN.msi)

  11. 服务:设置“ASP.NET State Service”自动启动

  12. IIS 日志:路径(如 D:\wwwlogs),每小时(统一设置一个全局的就行了,不需要设置每个网站),按需勾选“使用本地时间进行文件命名和滚动更新”

  13. IIS 导入证书:个人、允许导出证书。

  14. 设置默认网站的 https

  15. 设置权限:设置网站所在分区(如 D 盘),安全,添加 IIS_IUSRS,全部拒绝(防止跨站)

  16. 添加用户:为每个网站创建用户(既能防止跨站,又能跟踪进程),密码不能改、不过期,仅隶属于 IIS_IUSRS,并添加到每个网站的根目录,若用户创建失败看这里

  17. 创建网站:设置访问物理路径的用户;设置应用程序池的“标识”用户;编辑绑定:勾选需要服务器名称指示;检查域名是否绑全;设置写入目录;

  18. 重复上面两步

  19. 检查所有网站用户是否仅隶属于 IIS_IUSRS(在“组”页面双击 Users 和 IIS_IUSRS 查看成员)

  20. 在应用程序池列表页面检查 CLR 版本、管托管道模式和标识;在网站列表页面检查绑定和路径

  21. 设置 Web 部署

  22. 设置“IP 地址和域限制”

  23. 废弃旧服时再次检查:IIS 中各功能设置、hosts、安装的应用程序、启动项、服务、防火墙等

  24. 更改默认端口

  25. 解析各网站域名

  26. 其它:资源管理器-查看-选项-查看-去掉“始终显示图标,从不显示缩略图”前的勾

  27. 再次检查阿里云设置

  28. 私网中若有 ECS 的 hosts 中域名直接绑定到私网 IP 的,做相应更改

  29. 在备份工具中添加该服务器的所有备份项


>> 关于域名解析

因各地域名解析生效时间不可控,一般国内域名 1 天内,国际域名 2 天内。

  • 若网站数据库在 RDS、上传文件在 OSS,则解析 48 小时后直接停止原网站即可;(比较理想的)

  • 文件上传到 ECS 的可使用 FTP 等工具定时同步文件,或直接停止原网站。(网友会遇到新文章中图片无法显示等问题)

  • 还有一种方法是新网站提前解析一个备用域名,确保完全生效后再修改正式域名的解析,原网站无条件跳转到备用域名,如果数据库中有保存完整网址路径的,关闭原网站并解绑备用域名之后,进行批量替换。(缺点是可能会影响在搜索引擎的网站权重)

  • 部分有定时器的网站要注意,如果两个网站的定时器都正常开启会导致意外的,需要停止其中一个网站的定时器。

当然每种方法都有优缺点,选择可以接受且方便的一种即可。


更多文章:

从零搭建一台阿里云 ECS(Alibaba Cloud Linux / CentOS / Linux)并迁移网站

xoyozo 1 年前
2,809
  1. 下载 Senparc 官方 Sample


  2. 打开:\WeiXinMPSDK-master\src\Senparc.Weixin.MP.Sample\Senparc.Weixin.MP.Sample.sln


  3. 工具 - NuGet 包管理器 - 管理解决方案的 NuGet 程序包 - 更新

    选择所有的包(Microsoft.Net.Http 无法更新,暂不勾选),更新

    Microsoft.Net.Http 是个老顽固,无法更新也无法卸载,原因是它的语言包 Microsoft.Net.Http.zh-Hans 无法安装。我们切换到“已安装”选项卡,搜索“Microsoft.Net.Http”,选中 Microsoft.Net.Http.zh-Hans 并卸载它,现在我们可以更新 Microsoft.Net.Http 了。


  4. 【可能】运行报错:

    “/”应用程序中的服务器错误。

    配置错误

    说明: 在处理向该请求提供服务所需的配置文件时出错。请检查下面的特定错误详细信息并适当地修改配置文件。 

    分析器错误消息: 创建 system.web.webPages.razor/host 的配置节处理程序时出错: 未能加载文件或程序集“System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)

    源错误: 

    行 4:    <configSections>
    行 5:      <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
    行 6:        <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    行 7:        <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    行 8:      </sectionGroup>


    源文件: E:\WeiXinMPSDK-master\src\Senparc.Weixin.MP.Sample\Senparc.Weixin.MP.Sample\views\web.config    行: 


    展开 Senparc.Weixin.MP.Sample 引用,查看 System.Web.Razor 的属性,复制版本号。打开 \views\web.config,修改 System.Web.WebPages.Razor 的 Version(有 3 处)。


  5. 对于小型开发项目来说,我习惯将所有代码放在同一个项目(Project)中来,所以做了以下处理:(盛派官方分离这些代码是为了同时供 MVC 和 WebForms 重用)

    在 Senparc.Weixin.MP.Sample 中添加文件夹 CommonService,将 Senparc.Weixin.MP.Sample.CommonService 项目中的文件夹和文件(.config 除外)拖到 CommonService 文件夹中。

    卸载 Senparc.Weixin.MP.Sample.CommonService 项目并删除引用。


  6. 在解决方案的 Libraries 文件夹中,我们看到 Sample 项目了以下功能模块,这些就是 Senparc.Weixin 的源代码,如果不需要修改,可以卸载它们改为使用从 NuGet 获取,以便随时更新到最新版本:

    Senparc.WebSocket

    Senparc.Weixin

    Senparc.Weixin.MP

    Senparc.Weixin.MP.MvcExtension NuGet 中对应的包名为:Senparc.Weixin.MP.MVC

    Senparc.Weixin.Open

    Senparc.Weixin.Work

    Senparc.Weixin.WxOpen

    Senparc.Weixin.Cache.Memcached

    Senparc.Weixin.Cache.Redis

    卸载这些项目,并在 NuGet 中安装这些模块(搜索“Senparc.Weixin”第一页都在了,认准作者 Jeffrey Su)

    单元测试项目中的引用也按需更换,当然也可以卸载这些单元测试项目(视情况保留 Senparc.Weixin.MP.Sample.Tests) 。


  7. 【可能】运行报错


    HTTP Error 500.19 - Internal Server Error

    无法访问请求的页面,因为该页的相关配置数据无效。

    配置源:

    134:     </sessionState>

    135:   <sessionState customProvider="Memcached" mode="Custom">

    136: <providers>


    可以在 Global.asax 的 RegisterWeixinCache() 中修改 Memcached 配置。

    或者打开 Web.config,找到 Memcached 所在的 sessionState 标签,注释掉,这样程序自动使用上方的 InProc(应用进程内)。

    如果开启了 ASP.NET State Service 服务,那么建议改用 StateServer,与 Custom 一样需要序列化,方便日后改用 Memcached 或 Redis。


  8. 再次更新 NuGet,否则会提示无法加载 Enyim.Caching 的错误(报错行:MemcachedObjectCacheStrategy.RegisterServerList(memcachedConfig);)。


  9. 接下来,进入微信公众平台,在左侧 开发 - 基本配置 中设置相关参数(填写“服务器地址(URL)”如“https://weixin.xoyozo.net/weixin/”),打开 Sample 项目中的 Web.config,配置我们的公众号参数。


  10. 将项目发布到服务器上,尝试向公众号发送消息吧。


  11. 单元测试

    设置单元测试中的公众号配置项:打开 Senparc.Weixin.MP.Test/CommonAPIs/CommonApiTest.cs,在 AppConfig 方法中可以看到支持两种配置方式,在 Senparc.Weixin.MP.Test/Config/test.config 配置文件中配置,或直接在 CommonApiTest.cs 文件中配置,前者格式如下,后者建议只在个人测试项目中使用。

    <Config>
      <AppId>YourAppId</AppId>
      <Secret>YourSecret</Secret>
      <MchId>YourMchId</MchId>
      <TenPayKey>YourTenPayKey</TenPayKey>
      <TenPayCertPath>YourTenPayCertPath</TenPayCertPath>
      <!-- 小程序 -->
      <WxOpenAppId>YourOpenAppId</WxOpenAppId>
      <WxOpenSecret>YourWxOpenSecret</WxOpenSecret>
    </Config>

    另外,将 _testOpenId 值改为自己的微信号在本公众号上的 openid。


  12. 微信网页授权(OAuth2)

    打开 OAuth2Controller 控制器,将“Index”Action 中的盛派的网址(有 2 处)改成:

    Request.Url.Scheme + "://" + Request.Url.Authority + "/Oauth2/……


  13. 未完待续……

xoyozo 7 年前
6,240

tgideas-html5-api-test-1-1

编者按:今天腾讯万技师同学的这篇技术总结必须强烈安利下,目录清晰,层次分明,每个接口都有对应的简介、系统要求、实例、核心代码以及超实用的思维发散,帮你直观把这些知识点get起来。以现在HTML 5的势头,同志们,你看到的这些,可都是钱呐。

十二年前,无论多么复杂的布局,在我们神奇的table面前,都不是问题;

十年前,阿捷的一本《网站重构》,为我们开启了新的篇章;

八年前,我们研究yahoo.com,惊叹它在IE5下都表现得如此完美;

六年前,Web标准化成了我们的基础技能,我们开始研究网站性能优化;

四年前,我们开始研究自动化工具,自动化测试,谁没玩过nodejs都不好意思说是页面仔;

二年前,各种终端风起云涌,响应式、APP开发都成为了我们研究的范围,CSS3动画开始风靡;

如今,CSS3动画、Canvas、SVG、甚至webGL你已经非常熟悉,你是否开始探寻,接下来,我们可以玩什么,来为我们项目带来一丝新意?

没错,本文就是以HTML5 Device API为核心,对HTML5的一些新接口作了一个完整的测试,希望能让大家有所启发。

目录:

一、让音乐随心而动 – 音频处理 Web audio API
二、捕捉用户摄像头 – 媒体流 Media Capture
三、你是逗逼? – 语音识别 Web Speech API
四、让我尽情呵护你 – 设备电量 Battery API
五、获取用户位置 – 地理位置 Geolocation API
六、把用户捧在手心 – 环境光 Ambient Light API
七、陀螺仪 Deviceorientation
八、Websocket
九、NFC
十、震动 - Vibration API
十一、网络环境 Connection API

一、让音乐随心而动 – 音频处理 Web audio API

简介:

Audio对象提供的只是音频文件的播放,而Web Audio则是给了开发者对音频数据进行分析、处理的能力,比如混音、过滤。

系统要求:

ios6+、android chrome、android firefox

实例:

http://sy.qq.com/brucewan/device-api/web-audio.html

核心代码:

var context = new webkitAudioContext();
var source = context.createBufferSource();   // 创建一个声音源
source.buffer = buffer;   // 告诉该源播放何物 
createBufferSourcesource.connect(context.destination);   // 将该源与硬件相连
source.start(0); //播放

技术分析:

当我们加载完音频数据后,我们将创建一个全局的AudioContext对象来对音频进行处理,AudioContext可以创建各种不同功能类型的音频节点AudioNode,比如

1、源节点(source node)

我们可以使用两种方式加载音频数据:
<1>、audio标签

var sound, audio = new Audio();
audio.addEventListener('canplay', function() {
 sound = context.createMediaElementSource(audio);
 sound.connect(context.destination);
});
audio.src = '/audio.mp3';

<2>、XMLHttpRequest

var sound, context = createAudioContext();
var audioURl = '/audio.mp3'; // 音频文件URL
var xhr = new XMLHttpRequest();
xhr.open('GET', audioURL, true);
xhr.responseType = 'arraybuffer'; 
xhr.onload = function() {
 context.decodeAudioData(request.response, function (buffer) {
 source = context.createBufferSource();
 source.buffer = buffer;
 source.connect(context.destination);
 }
}
xhr.send();

2、分析节点(analyser node)

我们可以使用AnalyserNode来对音谱进行分析,例如:

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);

function draw() {
 drawVisual = requestAnimationFrame(draw);
 analyser.getByteTimeDomainData(dataArray);
 // 将dataArray数据以canvas方式渲染出来
};

draw();

3、处理节点(gain node、panner node、wave shaper node、delay node、convolver node等)

不同的处理节点有不同的作用,比如使用BiquadFilterNode调整音色(大量滤波器)、使用ChannelSplitterNode分割左右声道、使用GainNode调整增益值实现音乐淡入淡出等等。

需要了解更多的音频节点可能参考:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API

4、目的节点(destination node)

所有被渲染音频流到达的最终地点

思维发散:

1、可以让CSS3动画跟随背景音乐舞动,可以为我们的网页增色不少;
2、可以尝试制作H5酷酷的变声应用,增加与用户的互动;
3、甚至可以尝试H5音乐创作。

看看google的创意:http://v.youku.com/v_show/id_XNTk0MjQyNDMy.html

二、捕捉用户摄像头 – 媒体流 Media Capture

简介:

通过getUserMedia捕捉用户摄像头获取视频流和通过麦克风获取用户声音。

系统要求:

android chrome、android firefox

实例:

捕获用户摄像头 捕获用户麦克风

http://sy.qq.com/brucewan/device-api/camera.html

http://sy.qq.com/brucewan/device-api/microphone-usermedia.html

核心代码:

1、摄像头捕捉

navigator.webkitGetUserMedia ({video: true}, function(stream) {
 video.src = window.URL.createObjectURL(stream);
 localMediaStream = stream;
}, function(e){

})

2、从视频流中拍照

btnCapture.addEventListener('touchend', function(){
	if (localMediaStream) {
		canvas.setAttribute('width', video.videoWidth);
		canvas.setAttribute('height', video.videoHeight);
		ctx.drawImage(video, 0, 0);
	}
}, false);

3、用户声音录制

navigator.getUserMedia({audio:true}, function(e) {
	context = new audioContext();
	audioInput = context.createMediaStreamSource(e);	
	volume = context.createGain();
	recorder = context.createScriptProcessor(2048, 2, 2);
	recorder.onaudioprocess = function(e){
		recordingLength += 2048;
		recorder.connect (context.destination); 
	}	
}, function(error){});

4、保存用户录制的声音

var buffer = new ArrayBuffer(44 + interleaved.length * 2);
var view = new DataView(buffer);
fileReader.readAsDataURL(blob); // android chrome audio不支持blob
… audio.src = event.target.result;

思维发散:

1、从视频拍照自定义头像;
2、H5视频聊天;
3、结合canvas完成好玩的照片合成及处理;
4、结合Web Audio制作有意思变声应用。

三、你是逗逼? – 语音识别 Web Speech API简介:

1、将文本转换成语音;
2、将语音识别为文本。

系统要求:
ios7+,android chrome,android firefox

测试实例:

http://sy.qq.com/brucewan/device-api/microphone-webspeech.html

核心代码:

1、文本转换成语音,使用SpeechSynthesisUtterance对象;

var msg = new SpeechSynthesisUtterance();
var voices = window.speechSynthesis.getVoices();
msg.volume = 1; // 0 to 1
msg.text = ‘识别的文本内容’;
msg.lang = 'en-US';
speechSynthesis.speak(msg);

2、语音转换为文本,使用SpeechRecognition对象。

var newRecognition = new webkitSpeechRecognition();
newRecognition.onresult = function(event){
	var interim_transcript = ''; 
	for (var i = event.resultIndex; i < event.results.length; ++i) {
		final_transcript += event.results[i][0].transcript;
	}
};

测试结论:

1、Android支持不稳定;语音识别测试失败(暂且认为是某些内置接口被墙所致)。

思维发散:

1、当语音识别成为可能,那声音控制将可以展示其强大的功能。在某些场景,比如开车、网络电视,声音控制将大大改善用户体验;
2、H5游戏中最终分数播报,股票信息实时声音提示,Web Speech都可以大放异彩。

 四、让我尽情呵护你 – 设备电量 Battery API简介:

查询用户设备电量及是否正在充电。

系统要求:

android firefox

测试实例:

http://sy.qq.com/brucewan/device-api/battery.html

核心代码:

var battery = navigator.battery || navigator.webkitBattery || navigator.mozBattery || navigator.msBattery;
var str = '';
if (battery) {
 str += '<p>你的浏览器支持HTML5 Battery API</p>';
 if(battery.charging) {
 str += '<p>你的设备正在充电</p>';
} else {
 str += '<p>你的设备未处于充电状态</p>';
}
 str += '<p>你的设备剩余'+ parseInt(battery.level*100)+'%的电量</p>';
} else {
 str += '<p>你的浏览器不支持HTML5 Battery API</p>';
}

测试结论:

1、QQ浏览器与UC浏览器支持该接口,但未正确显示设备电池信息;
2、caniuse显示android chrome42支持该接口,实测不支持。

思维发散:

相对而言,我觉得这个接口有些鸡肋。
很显然,并不合适用HTML5做电池管理方面的工作,它所提供的权限也很有限。
我们只能尝试做一些优化用户体验的工作,当用户设备电量不足时,进入省电模式,比如停用滤镜、摄像头开启、webGL、减少网络请求等。

 五、获取用户位置 – 地理位置 Geolocation简介:

Geolocation API用于将用户当前地理位置信息共享给信任的站点,目前主流移动设备都能够支持。

系统要求:

ios6+、android2.3+

测试实例:

http://sy.qq.com/brucewan/device-api/geolocation.html

核心代码:

var domInfo = $("#info");

// 获取位置坐标
if (navigator.geolocation) {
	navigator.geolocation.getCurrentPosition(showPosition,showError);
}
else{
	domInfo.innerHTML="抱歉,你的浏览器不支持地理定位!";
}

// 使用腾讯地图显示位置
function showPosition(position) {
	var lat=position.coords.latitude;
	var lon=position.coords.longitude;

	mapholder = $('#mapholder')
	mapholder.style.height='250px';
	mapholder.style.width = document.documentElement.clientWidth + 'px';

	var center = new soso.maps.LatLng(lat, lon);
	var map = new soso.maps.Map(mapholder,{
		center: center,
		zoomLevel: 13
	});

	var geolocation = new soso.maps.Geolocation();
	var marker = null;
	geolocation.position({}, function(results, status) {
		console.log(results);
		var city = $("#info");
		if (status == soso.maps.GeolocationStatus.OK) {
			map.setCenter(results.latLng);
			domInfo.innerHTML = '你当前所在城市: ' + results.name;
		if (marker != null) {
			marker.setMap(null);
		}
		// 设置标记
		marker = new soso.maps.Marker({
			map: map,
			position:results.latLng
		});
		} else {
			alert("检索没有结果,原因: " + status);
		}
	});
}

测试结论:

1、Geolocation API的位置信息来源包括GPS、IP地址、RFID、WIFI和蓝牙的MAC地址、以及GSM/CDMS的ID等等。规范中没有规定使用这些设备的先后顺序。
2、初测3g环境下比wifi环境理定位更准确;
3、测试三星 GT-S6358(android2.3) geolocation存在,但显示位置信息不可用POSITION_UNAVAILABLE。

六、把用户捧在手心 – 环境光 Ambient Light简介:

Ambient Light API定义了一些事件,这些时间可以提供源于周围光亮程度的信息,这通常是由设备的光感应器来测量的。设备的光感应器会提取出辉度信息。

系统要求:

android firefox

测试实例:

http://sy.qq.com/brucewan/device-api/ambient-light.html

核心代码:

这段代码实现感应用前当前环境光强度,调整网页背景和文字颜色。


var domInfo = $('#info');
if (!('ondevicelight' in window)) {
	domInfo.innerHTML = '你的设备不支持环境光Ambient Light API';
} else {
	var lightValue = document.getElementById('dl-value');
	window.addEventListener('devicelight', function(event) {
		domInfo.innerHTML = '当前环境光线强度为:' + Math.round(event.value) + 'lux';
		var backgroundColor = 'rgba(0,0,0,'+(1-event.value/100) +')';
		document.body.style.backgroundColor = backgroundColor;
		if(event.value < 50) {
			document.body.style.color = '#fff'
		} else {
			document.body.style.color = '#000'
		}
	});
}

思维发散:

该接口适合的范围很窄,却能做出很贴心的用户体验。

1、当我们根据Ambient Light强度、陀螺仪信息、当地时间判断出用户正躺在床上准备入睡前在体验我们的产品,我们自然可以调整我们背景与文字颜色让用户感觉到舒适,我们还可以来一段安静的音乐,甚至使用Web Speech API播报当前时间,并说一声“晚安”,何其温馨;

2、该接口也可以应用于H5游戏场景,比如日落时分,我们可以在游戏中使用安静祥和的游戏场景;

3、当用户在工作时间将手机放在暗处,偷偷地瞄一眼股市行情的时候,我们可以用语音大声播报,“亲爱的,不用担心,你的股票中国中车马上就要跌停了”,多美的画面。

参考文献:
https://developer.mozilla.org/en-US/docs/Web/API
http://webaudiodemos.appspot.com/
http://www.w3.org/2009/dap/

转自 万技师 9 年前
3,871