首页
Search
1
Linux 下 Bash 脚本 bad interpreter 报错的解决方法
69 阅读
2
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
51 阅读
3
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
42 阅读
4
如何在 Clash for Windows 上配置服务
40 阅读
5
如何在 IOS Shadowrocket 上配置服务
40 阅读
clash
服务器
javascript
全部
游戏资讯
登录
Search
加速器之家
累计撰写
1,061
篇文章
累计收到
0
条评论
首页
栏目
clash
服务器
javascript
全部
游戏资讯
页面
搜索到
1061
篇与
的结果
2024-09-10
265行代码实现第一人称引擎
原文:A first-person engine in 265 lines(2014-6-11)今天,让我们进入一个可以伸手触摸的世界吧。在这篇文章里,我们将从零开始快速完成一次第一人称探索。本文没有涉及复杂的数学计算,只用到了光线投射技术。你可能已经见识过这种技术了,比如《上古卷轴2 : 匕首雨》、《毁灭公爵3D》还有 Notch Persson 最近在 ludum dare 上的参赛作品。Notch 认为它够好,我就认为它够好! 【Demo (arrow keys / touch)】【Source】用了光线投射就像开挂一样,作为一名懒得出油的程序员,我表示非常喜欢。你可以舒畅地浸入到3D环境中而不受“真3D”复杂性的束缚。举例来说,光线投射算法消耗线性时间,所以不用优化也可以加载一个巨大的世界,它执行的速度跟小型世界一样快。水平面被定义成简单的网格而不是多边形网面树,所以即使没有 3D 建模基础或数学博士学位也可以直接投入进去学习。利用这些技巧很容易就可以做一些让人嗨爆的事情。15分钟之后,你会到处拍下你办公室的墙壁,然后检查你的 HR 文档看有没有规则禁止“工作场所枪战建模”。玩家我们从何处投射光线?这就是玩家对象(Palyer)的作用,只需要三个属性 x,y,direction。function Player(x, y, direction) { this.x = x; this.y = y; this.direction = direction; }地图我们将地图存作简单的二维数组。数组中,0代表没墙,1代表有墙。你还可以做得更复杂些,比如给墙设任意高度,或者将多个墙高度数据——“楼层(stories)”打包进数组。但作为我们的第一次尝试,用0-1就足够了。function Map(size) { this.size = size; this.wallGrid = new Uint8Array(size * size); }投射一束光线这里就是窍门:光线投射引擎不会一次性绘制出整个场景。相反,它把场景分成独立的列然后一条一条地渲染。每一列都代表从玩家特定角度投射出的一条光线。如果光线碰到墙壁,引擎会计算玩家到墙的距离然后在该列中画出一个矩形。矩形的高度取决于光线的长度——越远则越短。绘画的光线越多,显示效果就会越平滑。1.找到每条光线的角度我们首先找出每条光线投射的角度。角度取决于三点:玩家面向的方向,摄像机的视野,还有正在绘画的列。var angle = this.fov * (column / this.resolution - 0.5); var ray = map.cast(player, player.direction + angle, this.range);2.通过网格跟踪每条光线接下来,我们要检查每条光线经过的墙。这里的目标是最终得出一个数组,列出了光线离开玩家后经过的每面墙。从玩家开始,我们找出最接近的横向(stepX)和纵向(stepY)网格坐标线。移到最近的地方然后检查是否有墙(inspect)。一直重复检查直到跟踪完每条线的所有长度。function ray(origin) { var stepX = step(sin, cos, origin.x, origin.y); var stepY = step(cos, sin, origin.y, origin.x, true); var nextStep = stepX.length2 range) return [origin]; return [origin].concat(ray(nextStep)); }寻找网格交点很简单:只需要对 x 向下取整(1,2,3...),然后乘以光线的斜率(rise/run)得出 y。var dx = run > 0 ? Math.floor(x + 1) - x : Math.ceil(x - 1) - x; var dy = dx * (rise / run);现在看出了这个算法的亮点没有?我们不用关心地图有多大!只需要关注网格上特定的点——与每帧的点数大致相同。样例中的地图是32×32,而32,000×32,000的地图一样跑得这么快!3.绘制一列跟踪完一条光线后,我们就要画出它在路径上经过的所有墙。var z = distance * Math.cos(angle); var wallHeight = this.height * height / z;我们通过墙高度的最大除以 z 来觉得它的高度。越远的墙,就画得越短。额,这里用 cos 是怎么回事?如果直接使用原来的距离,就会产生一种超广角的效果(鱼眼镜头)。为什么?想象你正面向一面墙,墙的左右边缘离你的距离比墙中心要远。于是原本直的墙中心就会膨胀起来了!为了以我们真实所见的效果去渲染墙面,我们通过投射的每条光线一起构建了一个三角形,通过 cos 算出垂直距离。如图:我向你保证,这里已经是本文最难的数学啦。渲染出来我们用摄像头对象 Camera 从玩家视角画出地图的每一帧。当我们从左往右扫过屏幕时它会负责渲染每一列。在绘制墙壁之前,我们先渲染一个天空盒(skybox)——就是一张大的背景图,有星星和地平线,画完墙后我们还会在前景放个武器。Camera.prototype.render = function(player, map) { this.drawSky(player.direction, map.skybox, map.light); this.drawColumns(player, map); this.drawWeapon(player.weapon, player.paces); };摄像机最重要的属性是分辨率(resolution)、视野(fov)和射程(range)。 分辨率决定了每帧要画多少列,即要投射多少条光线。 视野决定了我们能看的宽度,即光线的角度。 射程决定了我们能看多远,即光线长度的最大值 组合起来使用控制对象 Controls 监听方向键(和触摸事件)。使用游戏循环对象 GameLoop 调用 requestAnimationFrame 请求渲染帧。这里的 gameloop 只有三行oop.start(function frame(seconds) { map.update(seconds); player.update(controls.states, map, seconds); camera.render(player, map); });细节雨滴雨滴是用大量随机放置的短墙模拟的。var rainDrops = Math.pow(Math.random(), 3) * s; var rain = (rainDrops > 0) && this.project(0.1, angle, step.distance); ctx.fillStyle = '#ffffff'; ctx.globalAlpha = 0.15; while (--rainDrops > 0) ctx.fillRect(left, Math.random() * rain.top, 1, rain.height);这里没有画出墙完全的宽度,而是画了一个像素点的宽度。照明和闪电照明其实就是明暗处理。所有的墙都是以完全亮度画出来,然后覆盖一个带有一定不透明度的黑色矩形。不透明度决定于距离与墙的方向(N/S/E/W)。ctx.fillStyle = '#000000'; ctx.globalAlpha = Math.max((step.distance + step.shading) / this.lightRange - map.light, 0); ctx.fillRect(left, wall.top, width, wall.height);要模拟闪电,map.light 随机达到2然后再快速地淡出。碰撞检测要防止玩家穿墙,我们只要用他要到的位置跟地图比较。分开检查 x 和 y 玩家就可以靠着墙滑行。Player.prototype.walk = function(distance, map) { var dx = Math.cos(this.direction) * distance; var dy = Math.sin(this.direction) * distance; if (map.get(this.x + dx, this.y)
2024年09月10日
3 阅读
0 评论
0 点赞
2024-09-10
Qt Quick 笔记 - Anchors
看到许多 Qt Quick 样例中都会用到 Anchors,在官网这里找到了资料。还有这里也有详细的说明。除了Grid, Row, and Column,Anchors 也可以用来布局。注意只能对兄弟或父母 item 使用 anchorsAnchors 是 Item 中的属性,所以大多数类型都可以使用 Anchors。一图胜千言:![edges_qml][edges_qml]简单的例子:// ... Rectangle { id: rect1 //... } Rectangle { id: rect2 anchors.left: rect1.right //... } //...布局就会是这样子:![edge1][edge1]换一下//... Rectangle { id: rect1 //... } Rectangle { id: rect2 anchors.left: rect1.right anchors.top: rect1.bottom //... } //...布局就会是这样子![edge3][edge3]anchors.baseline 是一条假想的线,代表文本的位置。对于无文本的 item 它跟 top 一致。还有一些比较方便的属性anchors.fill: something 就是去填满 something,即 left,right,top, bottom 值都相等。anchors.centerIn: something 就是中心与 something 相等,即 verticalCenter, horizontalCenter 值相等。anchors.margins: 实数 以像素为单位设置间距,再次一图胜千言:![margins_qml][margins_qml]例子//... Rectangle { id: rect1 //... } Rectangle { id: rect2 anchors.left: rect1.right anchors.leftMargin: 5 //... } //...![edge2][edge2]注意 margins 是按 anchors 来设置间距,假如没有设置 anchor,margins 就会失效。此时可以用偏移值来设置间距,共三种: anchors.horizontalCenterOffset : 实数 anchors.verticalCenterOffset : 实数 anchors.baselineOffset : 实数 使用 AnchorChanges 去处理状态State { name: "anchorRight" AnchorChanges { target: rect2 anchors.right: parent.right anchors.left: undefined //remove the left anchor } }使用 AnchorAnimation 激活 AnchorChanges: Transition { AnchorAnimation {} //animates any AnchorChanges in the corresponding state change }![anchor_ordering][anchor_ordering]可以直接用 JavaScript 代替,但是要小心使用,会有很多坑 //会出问题 Rectangle { width: 50 anchors.left: parent.left function reanchorToRight() { anchors.right = parent.right anchors.left = undefined } }会变成这样:![anchor_ordering_bad][anchor_ordering_bad]要先释放不需要的属性再去更新其它属性Rectangle { width: 50 anchors.left: parent.left function reanchorToRight() { anchors.left = undefined anchors.right = parent.right } }还有这种代码//bad code Rectangle { width: 50; height: 50 anchors.left: state == "right" ? undefined : parent.left; anchors.right: state == "right" ? parent.right : undefined; }不推荐这么写,因为在 AnchorChanges 内部就会自动处理了。最后说明,不要跟绝对坐标混用,要用绝对坐标时先把冲突的 anchor 设置为 undefined [edges_qml]: /img/post/qt-quick-anchors/edgesqml.jpg [edge1]: /img/post/qt-quick-anchors/edge1.jpg [edge3]: /img/post/qt-quick-anchors/edge3.jpg [marginsqml]: /img/post/qt-quick-anchors/marginsqml.jpg [edge2]: /img/post/qt-quick-anchors/edge2.jpg [anchorordering]: /img/post/qt-quick-anchors/anchorordering.jpg [anchororderingbad]: /img/post/qt-quick-anchors/anchorordering_bad.jpg
2024年09月10日
4 阅读
0 评论
0 点赞
2024-09-10
使用 Express 与 AbsurdJS 构建 Node.js 应用
原文:Node.js application made with Express and AbsurdJS(2014-7-7)当今有许多新技术吸引着越来越多的开发者,Node.js 便是其中之一。主要因为它是 JavaScript 驱动的,许多人都很感兴趣。在本教程中,我将会教你结合 Express 去使用 AbsurdJS。Express 是流行的 Node.js 框架,而 AbsurdJS 则比较新鲜,希望你看完后会发现它很有用。本文中出现的源码都可以从这里下载。简介正如我提到的,Express 很流行。因为它是最早的 Node.js 框架之一。它把所有琐碎的事情都包办了,比如路由选择、参数解析、模板、向浏览器发送响应。它的库基于 Connect 提供的中间件架构对原生的 Node.js 进行了较好的封装。AbsurdJS 一开始是一个 CSS 预处理器。目的是为 CSS 开发者带来更好的灵活性。它接受纯 JavaScript 代码并转换为 CSS。大家对它的反馈都比较积极,我也在努力继续完善它。现在它不仅能预处理 CSS,还可以处理 HTML。它接受 JSON 和 YAML 并成功导出作客户端使用。步骤为了使最后做出的项目能跑起来,我们需要安装 Node.js。只需打开 http://nodejs.org 并点击大大的绿色“INSTALL”按钮。下载并安装完成后你就可以调用 Node.js 脚本,用npm(Node 的包管理器)安装依赖包。为你的项目新建一个文件夹,里面再新建一个空的“package.json”文件。包管理器会以这个文件为起点去安装我们需要的包。我们只需两个包,所以 json 文件应该是这个样子:{ "name": "AbsurdJSAndExpress", "description": "AbsurdJS + Express app", "version": "0.0.1", "dependencies": { "express": "3.4.4", "absurd": "*" } }当然,这里还有许多其它的参数可以配置,但为了方便举例我们就先按上面的配置吧。打开终端,定位包含到 Package.json 的目录,执行:npm install会在当前目录生成 node_modules 文件夹,并自动下载 Express 和 AbsurdJS。运行服务器有了 Express 你只需要简单几行代码就可以运行一个 http 服务器。var express = require('express'), app = express(); app.listen(3303); console.log('Listening on port 3303');保存以上代码为 app.js 并运行:node app.js这时控制台应该显示 “Listening on port 3303”。浏览器打开 http://localhost:3303/ 会看到:Cannot GET /不用担心,这很正常,因为我们还没有添加路由。添加路由Express 提供了友好的 API 去定义 URL 路由。我们在这里简单地添加一个,把下面的代码贴到 app.js 中。app.get('/', function(req, res, next) { res.setHeader('Content-Type', 'text/html'); res.send("application"); });这里做了几件事。.get 方法的第一个参数定义了路径;第二个参数是个方法,用来处理请求,它接受三个参数 – request、response、next。这里的好处是我们可以传入多个函数,它们会一个接一个的被调用。我们需要做的只是执行 next(),否者 next 方法不会被调用。比如:app.get( '/', function(req, res, next) { console.log("do some other stuff here"); next(); }, function(req, res, next) { res.send("application"); } );路由定义中通用的做法是添加一些可重用的任务作为中间件。比如说,我们有两种 Content-Type, HTML 和 CSS。用下面的方法就显得更加灵活。var setContentType = function(type) { return function(req, res, next) { res.setHeader('Content-Type', 'text/' + type); next(); } } app.get('/', setContentType("html"), function(req, res, next) { res.send("application"); });如果我们需要提供 CSS,只要用 setContentType(“css”) 即可。提供 HTML很多 Express 的教程和文章都会介绍一些模板引擎。通常是 Jade、Hogan 或者 Mustache。然而,如果用 AbsurdJS 我们不需要模板引擎。我们可以用纯 JavaScript 编写 HTML。更具体的说,是用 JavaScript 对象。我们先从实现着陆页开始。新建文件夹 pages,在里面新建 landing.js 文件。我们在用 Node.js 所以文件里应该包含:module.exports = function(api) { // ... }注意返回的函数接受 AbsurdJS API 的引用。这正是我们所要的。现在再加点 HTML:module.exports = function(api) { api.add({ _:"", html: { head: { 'meta[http-equiv="Content-Type" content="text/html; "]': {}, 'link[rel="stylesheet" type="text/css" href="https://blog.crimx.com//2014/07/07/nodejs-application-made-with-express-and-absurdjs/styles.css"]': {} }, body: {} } }); }“_” 属性添加的字符串在编译成HTML时不会被转换;其它的属性各定义了一个标签。还可以用其它方法去定义标签属性,但我认为用上面的方式更好。这个想法是从 Sublime 的 Emmet 插件中获得的。编译完成后会生成: 本次教程只有一个页面,而在现实中你可能会在多个页面中使用相同的HTML。此时更合理的做法是将这部分代码移到外部的文件中,在需要的时候引用进来。当然,这里还可以使用可重复模板。创建文件 /pages/partials/layout.js :module.exports = function(title) { return { _:"", html: { head: { 'meta[http-equiv="Content-Type" content="text/html; charset=utf-8"]': {}, 'link[rel="stylesheet" type="text/css" href="https://blog.crimx.com//2014/07/07/nodejs-application-made-with-express-and-absurdjs/styles.css"]': {}, title: title }, body: {} } }; };这里其实就是一个返回对象的函数。所以,之前的 landing.js 可以修改为:module.exports = function(api) { var layout = require("./partials/layout.js")("Home page"); api.add(layout); }可以看到,layout 模板接受 title 变量。这样就可以动态地生成一部分内容。接下来是向 body 标签添加内容。非常简单,因为所有内容都是纯 JavaScript 对象。module.exports = function(api) { var layout = require("./partials/layout.js")("Home page"); layout.html.body = { h1: "Page content" } api.add(layout); }生成的结果: Home page Page content 本文的代码看起来都很短、不完整,是因为全写的话文章就太长了。接下来我只会介绍一下建立无序列表的思想,因为这里比较有意思。剩余的部分跟 layout 类似,就不再敖述。下面就是生成无序列表的片段。标签:module.exports = function(data) { var html = { ul: [] }; for(var i=0; item=data[i]; i++) { html.ul.push({ li: item }); } return html; }这里只用了一个 ul 属性定义一个对象。ul 其实就是一个数组,装满列表中的项。var list = require("./partials/list.js"); var link = require("./partials/link.js"); list([ link("http://krasimir.github.io/absurd", "Official library site"), link("https://github.com/krasimir/absurd", "Official repository") ]);link 也是片段,类似这样子:module.exports = function(href, label) { return { a: { _attrs: { href: href }, _: label } } }组合起来后就会生成: Official library site Official repository 现在,想象我们有一堆可以使用的片段。如果它们编写得足够灵活,只需创建一次就可以在项目之间传递了。AbsurdJS 是如此强大,只要我们拥有一堆足够好的预定义集合,就可以快速、更具描述性地编写 HTNL 标记。最后,当 HTML 已经完成后,我们只需编译并发送给用户。于是,对 app.js 做小小的变动使得我们的应用响应正确的标记:var absurd = require("absurd"); var homePage = function(req, res, next) { absurd().morph("html").import(__dirname + "/pages/landing.js").compile(function(err, html) { res.send(html); }); } app.get('/', setContentType("html"), homePage);提供 CSS与 HTML 类型,先在 app.js 为 style.css 添加路由。var styles = function(req, res, next) { absurd().import(__dirname + "/styles/styles.js").compile(function(err, css) { res.send(css); }); } app.get('/styles.css', setContentType("css"), styles);使用 JavaScript 定义 CSS。任何东西都可以放在分好的 Node.js 模块中。让我们创建 /styles/styles.js 并加入代码:module.exports = function(api) { api.add({ body: { width: "100%", height: "100%", margin: 0, padding: 0, fontSize: "16px", fontFamily: "Arial", lineHeight: "30px" } }) }一个简单的 样式控制。注意带有破折号的属性被改写成驼峰式大小写风格。否则不能创建有效对象,会得警告。现在假设要控制 和 标签的样式。它们都是标题,颜色和字体相同;但是大小不一样。通过下面的方法,AbsurdJS会智能地输出正确的样式。var title = function() { return { color: "#2C6125", fontWeight: "normal", fontSize: "40px", fontFamily: "Georgia", padding: "20px 10px" } } api.add({ h1: title(), h2: [ title(), { fontSize: "30px" } ] });输出结果:h1, h2 { color: #2C6125; font-weight: normal; font-family: Georgia; padding: 20px 10px; } h1 { font-size: 40px; } h2 { font-size: 30px; }预处理器会收集只定义了一次的相同样式,并为不同的样式创建新的定义。结论如果打算使用 Node.js,Express 会是最好的起点之一。它虽然不是超级强大,但依然很好用。它具备了开发 web 应用所需的基本要素。然后去扩展它,使用 AbsurdJS 会为开发带来不少趣味性,因为整个应用都是用纯 JavaScript 编写的。资源 Express 官网 – http://expressjs.com/ Express GitHub 仓库 - https://github.com/visionmedia/express AbsurdJS 官网与在线编译器 – http://absurdjs.com AbsurdJS GitHub 仓库 – https://github.com/krasimir/absurd
2024年09月10日
3 阅读
0 评论
0 点赞
2024-09-10
Google Chrome 霸权主义
原文:Google Chrome HegemonyGoogle Chrome 是当今互联网中最大的玩家之一。它快速、可靠、功能丰富;特别对 web 开发者来说非常好用。Chrome 也允许安装第三方扩展;Google 团队做得很不错,这些扩展只需 HTML、CSS 和 JavaScript 即可搭建。本文将介绍几个能帮助我们开发的 Chrome 利器。本文的源码在[这里]。开发响应式 Web 应用现在响应式无处不在。随着移动设备的崛起,我们需要让应用运行在各种不同分辨率的平台上。新版本的 Chrome 提供了很棒的工具来帮助我们减轻工作量。这里我们先做一个简单的页面然后让它变成响应式。先是HTML代码: Responsive Web Design About Concept Examples Frameworks Contacts Responsive web design (RWD) is a web design approach ... The fluid grid concept calls for page element sizing to be in relative units ... 还有一些简单的 CSS 样式,浮动导航链接并让两个章节(section)彼此相邻。效果如图:准备好 HTML 和 CSS 后,我们就可以开始做些试验了。先学习添加媒体查询(Media Queries)断点。重点是要根据内容选择媒体并观察内容在什么范围会变得难看。不要因为流行就仅仅设置 1024x768 之类的分辨率。设置视图(Viewport)我们希望了解在视图多大的时候内容会断层。此时需要调整浏览器的窗口大小。在 Chrome 里,可以使用开发者工具面板直接调整大小。注意当我们改变视图大小的时候,右上角会显示当前的大小。这个小小的提示框省去了手动检查的麻烦。在我们的例子中,导航栏下的两个章节在 500px 左右就会显得过扁了。所以这里我们放置第一个媒体查询:section { float: left; width: 50%; } @media all and (max-width: 550px) { section { float: none; width: 100%; } }如果在 550px 再低一点,我们会发现 540px 左右导航栏会让窗口产生水平滚动条。再添加一个媒体查询解决这个问题:.nav { list-style: none; margin: 10px auto; padding: 0; width: 510px; } .nav li { float: left; margin: 0 20px 0 0; } @media all and (max-width: 540px) { .nav { width: auto; } .nav li { float: none; margin: 0; padding: 0; text-align: center; } }这就是一个适应多种分辨率的网页。虽然我们的页面很简陋,只有两个断点,但即便对于庞大的网站,选择断点的方法也是一样的。设备模拟有时候我们会收到 bug 显示应用在一些特定的设备上出错。Chrome 可以模拟各种各样的设备帮助我们解决问题。它会自动设置正确的分辨率和 HTTP 头;让我们方便地接近真实用户的视角。检测设备的 JavaScript 代码也会正常工作因为 Chrome 改变了请求报头。比方说我们需要模拟一部 iPhone5 设备。在开发者工具面板的顶栏有一个小小的抽屉(drawer)按钮,然后选择模拟(Emulation)标签。我们只需选择设备,Chrome 会自动应用所有的设置包括屏幕、用户代理(User agent)以及传感器;甚至还模拟了触摸事件。在元素面板(Elements Panel)进行修改我们的页面已经是响应式了,但有时候我们需要再做些修改。可以使用 Chrome 查看 document 上应用的样式。比如,第一个章节的文字太大了,我们希望做些修改并改变字体的颜色。查看具体的 CSS 规则时元素面板也很有用,但在这里我们不知道这些规则是在哪里定义的。这时在右侧的窗口就可以查看当前元素上经过计算后的样式,我们可以在这里去做修改。开发者工具面板有时候我们需要搜索特定的 CSS 样式,但是这个样式有太多定义了找起来很麻烦。在开发者工具面板中有一个很好用的过滤区域。比方说我们想查看 标签中应用 concept 类的规则。只需这么做:JavaScript 调试Google Chrome 是一个通用的工具。它有上一节提到的工具来支援设计师,也同样有支持开发者的工具。集成 JS 调试Chrome 集成了优秀的 JavaScript 调试器、控制台和源码查看器。为了说明这些工具是怎么工作的,我们先为例子添加一小段逻辑代码。我们希望点击 Examples 链接后该标签会变成 Awesome examples。借助 jQuery 可以让我们更好地关注到例子上。$('.nav').on('click', function(e) { var clicked = e.currentTarget; if(clicked.innerHTML === 'Examples') { clicked.innerHTML = 'Awesome examples'; } else { console.log('do nothing ...'); } });你很可能已经看出问题了,但且让我们运行以上代码看看。无论我们点击什么都会控制台都会打印 do nothing...。看来我们的 if 语句总是 false。先设置一个断点看看发生了什么。调试器在断点暂停并显示当前的局部变量。可以看到,clicked 变量指向的是导航栏的元素而不是 元素。所以它的 innerHTML 属性一定不是 Exmaples。这就是每次都打印 do nothing... 的原因。要修复这个 bug,将 .nav 改成 .nav a 即可。以上是调试的传统方法,只有在我们知道哪里设断点的情况下才有用。但如果我们在大型的代码库上工作,特别是要调试一连串文件的时候,问题就来了。我们开始到处放置 console.log 然后观察控制台。这个办法是可行,但很快就会产生大量的数据,这时就很难从中过滤出需要的信息。Chrome 对这个问题也提供了解决方案。我们可以在传给 .log 的文本前添加 %c ,然后利用第二个参数调整控制台输出的样式。如图:我们还可以添加一些其它的东西。console 对象有两个不太常见的方法 —— group 和 groupEnd。可以用来为 log 分组。使用 Deb.jsDeb.js 库结合了样式控制和输出分组。只需要在添加 .deb() 到需要检查的函数末尾之前把 Deb.js 包含进来即可。使用 .debc() 可以在控制台显示折叠的输出组。有了这个库,我们可以知道传给该函数的参数,函数的堆栈跟踪返回值和执行时间。正如上文提到的,这些信息被很好地组织和嵌套在一起,可以更方便地跟踪应用流。浏览器终端Google 浏览器的一个杀手锏是它的扩展应用生态系统。它为我们提供了途径去编写可安装的程序在浏览器中运行并提供了丰富的 API 给我们使用。最重要的是,我们不必再去学习一门新语言。只需要普通的 HTML、CSS 和 JavaScript 即可。看看这里《Chrome 扩展开发入门》。Yez!在 Chrome 网上应用店(Web Store)中甚至有一个单独的区域叫 Web development,里面有许多专门为我们开发者制作的有用工具。其中有一个工具叫 Yez!。它为开发者工具面板提供了类似终端的功能。可以执行 shell 命令和实时获得输出。该应用本身不足以执行 shell 命令,因为它没有权限访问操作系统。所以这里需要 Node.js 作为代理。Yez! 通过 web sockets 连接正在运行的 Node.js 应用。通过 Node.js 包管理器安装 Yez!:npm install -g yezYez! 集成 GitYez! 很好的整合了 Git,它会显示当前目录的分支。我们可以执行终端命令并立即得到输出。这个扩展原本是作为一个任务运行器开发的。所以它提供了任务定义的接口。实际上,这不过是一系列按顺序执行的 shell 命令。我们可以通过 shell 脚本实现同样的效果。我们还可以实时查看终端的输出,所以这个扩展非常适合开发 Node.js 应用。通常我们需要重启 Node.js 程序,但在 Chrome 中一切都是可见的。执行 HTTP 请求作为 web 开发者,我们经常需要让我们的应用执行 HTTP 请求。也许我们开发了一个 REST API,或者有一个接受 POST 参数的 PHP 脚本。这里有一个命令行工具 cURL。它应该是最常用的 web 查询工具了。有了 cURL,我们不再需要转到终端去。这里可以用 DHC (REST HTTP API Client)。这个应用可以让我们完全控制 HTTP 请求。我们可以改变请求方式、报头、或者 GET 和 POST 参数。它还可以显示请求的结果,连同报头一起显示,非常实用。使用 Chrome Web 驱动进行测试我们都知道测试的重要性。清楚了解我们的程序运行正确对我们来说极为重要。有时候测试的编写会很困难,特别是当我们要测试用户界面。幸运的是,这里有一个 Node.js 模块可以控制我们的浏览器(Chrome)并触发动作比如访问页面、点击链接或者填写表格。它就是 DalekJS。安装很简单:npm install -g dalek-cli我们做个小试验看看它是怎么工作的。在一个新建的目录中,我们需要一个 package.json 文件包含如下内容:{ "name": "project", "description": "description", "version": "0.0.1", "devDependencies": { "dalekjs": "0.0.8", "dalek-browser-chrome": "0.0.10" } }接着在这个目录执行:npm install我们会在 node_modules 文件夹中看到有 dalekjs 和 dalek-browser-chrome。回到原来的目录,新建文件 test.js,我们会在这里中编写我们的测试。这是一个小脚本用来测试 GitHub 的搜索功能:var url = 'https://github.com/'; module.exports = { 'should perform search in GitHub': function (test) { test .open(url) .type('#js-command-bar-field', 'dalek') .submit('#top_search_form') .assert.text('.repolist h3 a', 'dalekjs/dalek', 'There is a link with label dalekjs') .click('[href="https://blog.crimx.com/dalekjs/dalek"]') .assert.text('.repository-description p', 'DalekJS Base framework', 'It is the right repository') .done() } };要运行测试,我们需要在控制台中发射:dalek ./test.js -b chromeDalekJS 会启动 Google Chrome 浏览器的一个实例,然后打开 GitHub 网站,你可以在搜索框中输入 dalek ,它会跳转到正确的仓库。最后,Node.js 会把打开的窗口关闭。控制台的输出看起来是这样的:DalekJS 支持 PhantomJS、Firefox、InternetExplorer 和 Safari。是个很有用的工具,在Windows、Linux 和 Mac 上都可以工作。在官网 dalekjs.com 上可以获得文档。总结我们在电脑前花最多时间的就是看浏览器了。很高兴知道 Google Chrome 不仅是浏览网页的程序,还是一个强大的 web 开发工具。如今,这里有着大量的实用扩展和持续增长的社区。如果你没有打算用 Google Chrome 开发你的下一个 web 应用,我强烈建议你去试一下。
2024年09月10日
3 阅读
0 评论
0 点赞
2024-09-10
阅读 jQuery 源码的18个惊喜
原文:18 Surprises From Reading jQuery's Source Code(2014-7-23)我热爱 jQuery,且尽管我认为自己算是一名高级 JavaScript 开发者,我从来没有试过由头到尾把 jQuery 的源码看一遍,直到现在。这里分享一些我一路下来学到的东西:注意:我使用 $.fn.method() 语法来表示调用一组匹配元素的方法。比如当我说 $.fn.addClass,则表示 $('div').addClass('blue') 或者 $('a.active').addClass('in-use') 此类的用法。$.fn 是 jQuery 包装元素的原型。 Sizzle 的权重:Sizzle 是 jQuery 用于在 DOM 找元素的的选择器引擎,基于 CSS 选择器。正是它将 $('div.active') 转换成可操作的元素数组。我知道 Sizzle 占了 jQuery 相当大的部分,但它的庞大还是吓到了我。按行数来说它很无疑是 jQuery 中唯一最庞大的特性。我估计它占了总代码库的 22%,而第二大的特性—— $.ajax 只占了 8%。 $.grep:这个方法与 Underscore 的 _.filter 类似。接受两个参数,一个元素数组和一个函数,对每个元素依次执行函数,返回执行结果为 true 的元素数组。 冒泡禁令:jQuery 明文禁止 load 事件冒泡。从内部看,jQuery 在所有的 load 事件中传入特殊的 noBubble: true 标记,所以 image.load 事件才不会冒泡到 window 上错误地触发 window.load 事件。 默认动画速度:jQuery 通过快速连续地改变样式属性来实现元素动画效果。每一次小改变被称作一个 tick。默认动画速度是每13毫秒运行一次 tick,要改变速度你可以重写 jQuery.fx.interval 成你想要的整数。 fn.addClass 可以接受函数:我们通常向 $.fn.addClass 提供一个包含类名的字符串来创建元素。但它也可以接受一个函数。这个函数必须返回一个字符串,多个类名间要以空格隔开。这里还有个彩蛋,这个函数接受已匹配元素的索引作为参数,你可以用这个特性来构造智能变化的类名。 fn.removeClass 也一样:与上文的一样,它也可以接受一个函数。这个函数也会自动接收元素的索引。 :empty 伪选择器:可以方便地用来匹配没有孩子的元素。 :lt 与 :gt 伪选择器:它们会根据元素在匹配集合中的索引来匹配元素。比如 $('div:gt(2)') 会返回所有的 div,除了前三个(从0开始)。如果你传入一个负数,它会倒过来从尾开始数。 $(document).ready() 的承诺: jQuery 貌似是用回了自己的东西。在内部,可信赖的 $(document).ready() 使用了一个 jQuery 延迟来确定 DOM 在什么时候完全加载。 $.type: 大家肯定能熟练使用 typeof 来确定数据类型,但你知不知道 jQuery 提供了一个 .type() 方法?jQuery 版比原生版更加智能。比如 typeof (new Number(3)) 返回 object,而 $.type(new Number(3)) 则返回 number。更新:正如 ShirtlessKirk 在评论中指出,$.type 返回其对象的 .valueOf() 属性。所以更准确的说法应该是 $.type 告诉你一个对象的返回值的类型。 $.fn.queue:你可以通过 $(‘div’).queue() 查看一个元素的效果队列,很方便地了解元素还剩余多少效果。更有用的是,你可以直接操作队列去添加效果。从 jQuery 文档摘录的: $( document.body ).click(function() { $( "div" ) .show( "slow" ) .animate({ left: "+=200" }, 2000 ) .queue(function() { $( this ).addClass( "newcolor" ).dequeue(); }) .animate({ left: "-=200" }, 500 ) .queue(function() { $( this ).removeClass( "newcolor" ).dequeue(); }) .slideUp(); }); 禁用元素不会触发 click 事件:jQuery 默认不会为禁用的元素执行 click 事件,有了这个优化,你无需自己用代码再检查一遍。 $.fn.on 可以接受对象:你知道 $.fn.on 可以接受一个对象来一次过连接多个事件吗?jQuery 文档的例子: $( "div.test" ).on({ click: function() { $( this ).toggleClass( "active" ); }, mouseenter: function() { $( this ).addClass( "inside" ); }, mouseleave: function() { $( this ).removeClass( "inside" ); } }); $.camelCase:这个有用的方法可以将连字符式的字符串转换成驼峰式的字符串。 $.active:调用 $.active 返回 XHR (XML Http Request) 查询的个数。利用它可以手动制定 AJAX 请求的并发上限。 $.fn.parentsUntil / $.fn.nextUntil / $.fn.prevUntil:我比较熟悉 .parents()、.next() 和 .prev(),却不知道原来还有其它的方法。它们会匹配所有的 双亲/下一个/前一个 元素直到(until)遇到符合终止条件的元素。 $.fn.clone 参数:当你用 .clone() 克隆一个元素,你可以用 true 作为第一个参数来克隆该元素的数据属性(data attributes)和事件。 更多的 $.fn.clone 参数:除了上面的方法外,你还可以再传多一个 true 参数来克隆该元素所有孩子的数据属性和事件。这叫做“深克隆”。第二个参数的默认值与第一个一样(第一个默认false)。所以当第一个参数是 true 而你想让第二个参数也是 true 时,完全可以忽略第二个参数。
2024年09月10日
4 阅读
0 评论
0 点赞
1
...
106
107
108
...
213