Quantcast
Channel: CNode:Node.js专业中文社区
Viewing all 14821 articles
Browse latest View live

问个超级小白的问题。

$
0
0

/** 测试1*/ function test(aNu){ setTimeout(function(){ console.log(aNu) },1000*10) }

for (var index = 0; index < 10; index++) { test(index); }

最后输出的 结果是0-9 在第一次setTimeout等待执行的时候,外面的index应该等于9了。里面应该输出9次9才对-。-可是为啥是0-9呢~


koa2部署最佳实践--安全篇(Express 部署最佳实践-安全篇的补充)

$
0
0

最近看了zhangmingkai4315的Express 部署最佳实践-安全篇很赞,补充写了一个koa2的安全篇。

网站低权限

添加新用户

#首先登录 root 账号

#新建用户
useradd username
#更改用户密码,激活使用
passwd username

用pm2开机自启网站

切换到username目录下,并将网站源码拷贝到username的家目录下 新建pm2启动配置文件

cd youProject
npm i
vi run.json

run.json的内容

{
  "apps": [
      {
          "name": "projectName",
          "script": "./index.js",
          "args": [],
          "watch": true,
          "ignore_watch": [
              "node_modules",
              "public"
          ],
          "node_args": "",
          "exec_mode": "cluster",
          "merge_logs": true,
          "cwd": "/home/username/youProject",
          "env": {
            "NODE_ENV": "production",
            "PORT": 80
        }
      }
  ]
}

script: 项目启动文件 ignore_watch: 不监控的文件夹 cwd:项目的根目录 env:设置的环境变量

启动项目

pm2 start run.json

设置开机自启

pm2 startup

3333333.png

在root用户下执行提示的命令

# env PATH=$PATH:/usr/local/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u centos --hp /home/centos

在username用户下,执行

pm2 save

使用Helmet补充

在koa中使用:

# 安装
npm install koa-helmet --save

# 使用
const Koa = require('koa')
const helmet = require('koa-helmet')
const app = new Koa()
 
app.use(helmet())
 
app.use((ctx) => {
  ctx.body = 'Hello World'
})
 
app.listen(4000)

额外需要考虑的问题补充

1.使用提交速率限制,防止蛮力攻击,使用koa-limit来限制用户的提交和查询的速度。

# 安装
npm install koa-limit

# 使用
var koa = require('koa');
var favicon = require('koa-favicon');
var limit = require('koa-limit');
 
var app = koa();
// If you are using reverse proxy on the front of node, like 'nginx', please set this 
// app.proxy = true; 
app.use(favicon());
app.use(limit({
  limit: 1000,
  interval: 1000 * 60 * 60
}));
 
app.use(function *() {
  this.body = 'hello';
});
 
app.listen(7001);

2.使用csurf来阻止CSRF攻击

浅谈CSRF攻击方式 CSRF是什么?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

CSRF可以做什么?

你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…造成的问题包括:个人隐私泄露以及财产安全。

koa2中使用csurf 安装

npm install --save koa-csrf@3.x

服务器端

import Koa from 'koa';
import bodyParser from 'koa-bodyparser';
import session from 'koa-generic-session';
import convert from 'koa-convert';

const app = new Koa();

// set the session keys 
app.keys = [ 'a', 'b' ];

// add session support 
app.use(convert(session()));

// add body parsing 
app.use(bodyParser());

// add the CSRF middleware 
app.use(new CSRF({
 invalidSessionSecretMessage: 'Invalid session secret',
 invalidSessionSecretStatusCode: 403,
 invalidTokenMessage: 'Invalid CSRF token',
 invalidTokenStatusCode: 403,
 excludedMethods: [ 'GET', 'HEAD', 'OPTIONS' ],
 disableQuery: false
}));

// your middleware here (e.g. parse a form submit) 
app.use((ctx, next) => {

 if (![ 'GET', 'POST' ].includes(ctx.method))
   return next();

 if (ctx.method === 'GET') {
   ctx.body = ctx.csrf;
   return;
 }

 ctx.body = 'OK';

});

app.listen();

客户端pug模板

form(action='/register', method='POST')
  input(type='hidden', name='_csrf', value=csrf)
  input(type='email', name='email', placeholder='Email')
  input(type='password', name='password', placeholder='Password')
  button(type='submit') Register

Express 部署最佳实践-安全篇中的有些 还没有试到后面再补充。

欢迎大家一起讨论koa2安全,一起共同进步。

mongoose Schema 为了关联定义时几乎全用的ObjectId, 现在要做模糊搜索了,直接懵了。。大神求解

$
0
0

定义Schema如下: sender : {type: mongoose.Schema.Types.ObjectId, ref: ‘user’}, //发送人id receivers : [{type: mongoose.Schema.Types.ObjectId, ref: ‘user’}], //收件人id copiers : [{type: mongoose.Schema.Types.ObjectId, ref: ‘user’}], //抄送人id 数组 title : {type: String, default: ‘’}, content : {type: String, default: ‘’}, //邮件内容 html字符串 files : [{type: mongoose.Schema.Types.ObjectId, ref: ‘files’, default: ‘’}], //附件url地址 数组 这是个邮件部分的Schema,现在要对其做模糊搜索,发现用了ObjectId 没法搜索,,, 想法大概是这样写搜索的,虽然肯定是错的: await EmailModel.find({ $or: [ {sender.nickname: new RegExp(searchValue)}, {receivers.nickname: new RegExp(searchValue)}, {copiers.nickname: new RegExp(searchValue)}, ] }).populate([ {path: ‘sender’, select: ‘nickname’}, {path: ‘receivers’, select: ‘nickname’}, {path: ‘copiers’, select: ‘nickname’}, {path: ‘files’} ])…execAsync(); 请问下大神们,这种情况该怎么处理?

egg部署后怎么用nginx反向代理?

$
0
0

这是config的配置 QQ20171226-121645.png

这是nginx的配置 QQ20171226-121805.png

总是不成功

【知乎Live】狼叔:如何正确的学习Node.js

$
0
0

Live 简介 我是 i5ting,江湖人称“狼叔”,现供职于阿里巴巴,前去哪儿网前端架构师,斯达克学院( StuQ )明星讲师,Node.js 技术布道者。曾就职在新浪、网秦,曾做过前端、后端、数据分析、移动端负责人、做过首席架构师、技术总监,全栈技术实践者,目前主要关注技术架构和团队梯队建设方向。

本次 Live 是因为很多朋友问狼叔如何学习Node.js,新手和有其他语言背景的人都有,大家问我的原因大致有2个,1是我曾以Node布道者身份做过很多官方的事儿,2是因为我写了2年的书《更了不起的Node.js》,出版可能还要一段时间。每次大家提问我都特想回复,可是又不是三句二句能说清楚的。既然大家都有这样需求,我想我也是时候来总结一次了。至少作为入门参考,也是极好的。

我将围绕如何正确的学习和使用 Node.js 进行解读。Node.js学习有三等境界,可以简单,可以难,可以装逼。关于Node的书几乎都过时了,我该买哪本?Node用途那么多,我该从哪里学起?大前端变化那么快,如果能够做到每日精进?Node Web框架那么多,我该怎么选?作为 Node.js 这几年的蓬勃发展的亲历者和深度用户,狼叔带你回顾此事件,以全新视角来帮助大家更好解决正确的学习和使用 Node.js 的问题。

本次 Live 主要包括以下内容

• Node.js学习三等境界 • 关于Node的书几乎都过时了,我该买哪本? • Node用途那么多,我该从哪里学起? • 大前端变化那么快,如果能够做到每日精进? • Node Web框架那么多,我该怎么选? • 从招聘角度谈谈Node.js开发需要具备的技能

本次Live主要是科普,适用新用户和比较迷茫的Node朋友,参加地址

https://www.zhihu.com/lives/928687583372926976

[上海] 招聘资深 Node.js / 全栈工程师 25K~35K

$
0
0

我们能给你

  • 全 js技术栈,后端 node.js、前端 react native,可以深挖nodejs,也可向全栈方向发展
  • 使用丰富的国际前沿技术:aws、new relic、firebase、fabric、optimizely等

我们要求你

  1. 有 3 年以上的后端开发经验;
  2. 精通 JavaScript & Node.js;
  3. 熟悉分布式高可用高并发系统架构设计,能够独立设计后端系统架构;
  4. 对 MongoDB / Redis 有实操经验,了解 DB 设计和优化;
  5. 熟悉 TCP、UDP、HTTP 协议以及 Restful API;
  6. 熟悉单元测试,了解 TDD,熟悉常见测试技术;
  7. 了解常用重构方法,了解设计模式原则;
  8. 学习能力强,责任心强,对技术有热情和追求;
  9. 解决问题能力强,善于抽象,规约,简化问题。

加分项

  1. 在博客或 Github上 有技术沉淀;
  2. 熟悉 Docker 以及集群调度工具,有实际经验;
  3. 熟悉 TypeScript、Golang 或者 Python;
  4. 熟悉 Angular / React / Vue。

联系方式 邮箱:lugang@gmail.com 地址:上海市闵行区万源路2158号泓毅大厦B座

【Webpack】3.多入口设置与 html-webpack-pugin 插件详解

$
0
0

【Webpack】1.入门及简单使用【Webpack】2.四个核心概念及使用【Webpack】3.多入口设置与 html-webpack-pugin 插件详解

多入口设置与 html-webpack-pugin 插件详解(Demo3 Source)

  我们可以为 entry指定多个入口。在开始代码之前,我们需要创建如下目录解构

.        
├── index.html            // 显示的页面
├── main1.js             // webpack 入口1
├── main1.js            // webpack 入口2
├── style.css          // 样式文件
└── webpack.config.js // webpack 中默认的配置文件

  我们在 index.html文件中输入以下内容:

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>demo3</title>
    </head>
    <body>
        
    </body>
    </html>

  我们在 main1.js文件中输入以下内容:

    improt './style.css'
    
    var h1 = document.createElement('h1');
    h1.innertHTML = '这是 main1.js 中的内容';
    document.body.appendChild(h1);

  我们在 main2.js文件中输入以下内容:

    improt './style.css'
    
    var h2 = document.createElement('h2');
    h2.innertHTML = '这是 main2.js 中的内容';
    document.body.appendChild(h2);

  我们在 style.css文件中输入以下内容:

    h1{ color: red; }
    h2{ color: blue; }

  我们在 webpack.config.js文件中输入以下内容:

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const path = require('path');
    
    const config = {
        entry: {
            bundle1: './main1.js',
            bundle2: './main2.js',
        },
        output: {
            path: path.resolve(__dirname,'dist'),
            filename: '[name].js'
        },
        module: {
            rules: [
                { test: /\.css$/, loader: 'style-loader!css-loader' }
            ]
        },
        pugins: [
            new HtmlWebpackPlugin({ template: './index.html' })
        ]
    };
    
    module.exports = config;

  完成上面的代码工作后,运行 webapck命令,我们打开 dist文件中的 index.htmlindex.html 运行结果

运行的结果并不是我们预期的那样展示 h1的内容在前,h2内容在后,打开生成后的 index.html源码:

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>demo3</title>
    </head>
    <body>
        <script type="text/javascript" src="bundle2.js"></script>
        <script type="text/javascript" src="bundle1.js"></script>
    </body>
    </html>

从源码中便可得知,先引入的 bundle2.js文件,也就是 main2.js的内容,后引入的 bundle1.js文件,也就是 main1.js的内容。

  我们并没有在 index.html中输入任何引入 JavaScript文件的代码,那么使用 webpack打包后生成的文件,是怎么引入 JavaScript文件的呢。事实上就是通过 html-webpack-plugin为我们生成的 index.html

html-webpack-plugin中的参数详解

  通过 npm中的介绍,html-webpack-plugin是一个 webpack插件,可以简化 HTML文件的创建,为我们的 webpack包提供服务,它包含了一个改变每个编译的文件名参数。使用 lodash模板提供我们自己的模板或者使用自己的 loader

  我们可以配置以下参数传递给 HtmlWebpackPlugin

  • title: 用于生成的 HTML文档的标题。
  • filename: 要写入 HTML的文件。默认为 index.html。你也可以在这里指定一个子目录(例如:assets / admin.html)。
  • template: 引入的模板文件,具体内容可以查看文档
  • inject: true | 'head' | 'body' | false,指定引入 JavaScript脚本文件,在生成的HTML中的位置。默认为 true,指JavaScript脚本文件在 <body>元素中引入;head,指JavaScript脚本文件在 <head>元素中引入,bodytrue值相同;false指只生成 HTML文件,不引入任何JavaScript脚本文件。
  • favicon: 生成的 HTML文件中的图标路径。
  • minify: {...} | false是否对生成的 HTML文件压缩,默认为 false,具体配置可查看 html-minifier
  • hash: true | false,如果为 true,给生成的 js 文件一个独特的 hash 值,该 hash 值是该次 webpack 编译的 hash 值,这对缓存清除非常有用。默认值为 false
  • cache: true | false, 如果为 true则只编译生成更改的内容将文件,默认值为 true
  • showErrors:true | false,如果为 true,则将错误内容添加到 HTML中,默认值为 true
  • chunks: 指定引入的 JavaScript脚本文件(例如:[ ‘bundle1’, ‘bundle2’ ])。
  • chunksSortMode: 'none' | 'auto' | 'dependency' |'manual' | {function} - default: 'auto',对引入的 chunks进行排序,具体可以查看该文档
  • excludeChunks: 排除掉指定的 JavaScript脚本文件(例如:[ ‘bundle1’, ‘bundle2’ ])。
  • xhtml: true | false,默认值是 false,如果为 true ,则以兼容 xhtml的模式引用文件。

  现在我们知道了 html-webpack-plugin中的参数,下面我们就来修改 webpack.config.js中的内容:

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const path = require('path');

    const config = {
        entry: {
            bundle1: path.resolve(__dirname,'main1.js'),
            bundle2: path.resolve(__dirname,'main2.js')
        },
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: '[name].js'
        },
        module: {
            rules: [
                { test: /\.css$/, loader: 'style-loader!css-loader' }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({ 
                title: '多文件引入', // 生成 html 的标题
                filename: 'index.html',// 生成 html 文件的名称 
                template: path.resolve(__dirname,'index.html'), // 根据自己的指定的模板文件来生成特定的 html 文件 
                // inject: true, // 注入选项 有四个值 ture: 默认值,script标签位于html文件的 body 底部, body: 同 true, head: script标签位于html文件的 head 底部,false:不注入script标签
                favicon: path.resolve(__dirname,'favicon.ico'), // 生成的 html 文件设置 favicon
                minify: {
                    caseSensitive: false, //是否大小写敏感
                    collapseBooleanAttributes: true, //是否简写boolean格式的属性如:disabled="disabled" 简写为disabled 
                    collapseWhitespace: true //是否去除空格
                },
                hash: true, // hash选项的作用是 给生成的 js 文件一个独特的 hash 值,该 hash 值是该次 webpack 编译的 hash 值。默认值为 false
                cache: true, // 默认值是 true。表示只有在内容变化时才生成一个新的文件
                showErrors: true, // showErrors 的作用是,如果 webpack 编译出现错误,webpack会将错误信息包裹在一个 pre 标签内,属性的默认值为 true
                chunks: [ 'bundle1', 'bundle2' ], // 指定引入的 js 文件
                //excludeChunks:[ 'bundle1' ], // 排除掉某些 js 文件
                /**
                 * script 标签的引用顺序
                 * 'dependency' 按照不同文件的依赖关系来排序
                 * 'auto' 默认值,插件的内置的排序方式
                 * 'none'
                 * 'manual'
                 * funciton 自定义排序,与JS中自定义数组的sort回调一个含义, 具体可以看 https://github.com/jantimon/html-webpack-plugin/issues/481
                 */
                chunksSortMode: function(chunk1,chunk2){
                    var orders = [ 'bundle1' , 'bundle2' ];
                    var order1 = orders.indexOf(chunk1.names[0]);
                    var order2 = orders.indexOf(chunk2.names[0]);
                    return order1 - order2;
                },
                xhtml: false // 一个布尔值,默认值是 false ,如果为 true ,则以兼容 xhtml 的模式引用文件
            })
        ]
    };

module.exports = config;

  完成上面的代码工作后,运行 webapck命令,我们打开 dist 文件中的 index.html。 index.html 运行结果  Nice!与我们的预期效果显示一致。在对 html-webpack-plugin的介绍中,提到了 lodash模板, 那么该怎么用呢?我们再次修改 webpack.config.js中的内容,为 HtmlWebpackPlugin传入 Date参数:

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const path = require('path');

    const config = {
        entry: {
            bundle1: path.resolve(__dirname,'main1.js'),
            bundle2: path.resolve(__dirname,'main2.js')
        },
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: '[name].js'
        },
        module: {
            rules: [
                { test: /\.css$/, loader: 'style-loader!css-loader' }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({ 
                date: new Date(),
                title: '多文件引入', // 生成 html 的标题
                filename: 'index.html',// 生成 html 文件的名称 
                template: path.resolve(__dirname,'index.html'), // 根据自己的指定的模板文件来生成特定的 html 文件 
                // inject: true, // 注入选项 有四个值 ture: 默认值,script标签位于html文件的 body 底部, body: 同 true, head: script标签位于html文件的 head 底部,false:不注入script标签
                favicon: path.resolve(__dirname,'favicon.ico'), // 生成的 html 文件设置 favicon
                minify: {
                    caseSensitive: false, //是否大小写敏感
                    collapseBooleanAttributes: true, //是否简写boolean格式的属性如:disabled="disabled" 简写为disabled 
                    collapseWhitespace: true //是否去除空格
                },
                hash: true, // hash选项的作用是 给生成的 js 文件一个独特的 hash 值,该 hash 值是该次 webpack 编译的 hash 值。默认值为 false
                cache: true, // 默认值是 true。表示只有在内容变化时才生成一个新的文件
                showErrors: true, // showErrors 的作用是,如果 webpack 编译出现错误,webpack会将错误信息包裹在一个 pre 标签内,属性的默认值为 true
                chunks: [ 'bundle1', 'bundle2' ], // 指定引入的 js 文件
                //excludeChunks:[ 'bundle1' ], // 排除掉某些 js 文件
                /**
                 * script 标签的引用顺序
                 * 'dependency' 按照不同文件的依赖关系来排序
                 * 'auto' 默认值,插件的内置的排序方式
                 * 'none'
                 * 'manual'
                 * funciton 自定义排序,与JS中自定义数组的sort回调一个含义, 具体可以看 https://github.com/jantimon/html-webpack-plugin/issues/481
                 */
                chunksSortMode: function(chunk1,chunk2){
                    var orders = [ 'bundle1' , 'bundle2' ];
                    var order1 = orders.indexOf(chunk1.names[0]);
                    var order2 = orders.indexOf(chunk2.names[0]);
                    return order1 - order2;
                },
                xhtml: false // 一个布尔值,默认值是 false ,如果为 true ,则以兼容 xhtml 的模式引用文件
            })
        ]
    };

module.exports = config;

更改 index.html中的内容,lodash模板默认支持的是 ejs模板的语法:

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>demo3</title>
    </head>
    <body>
        <%= htmlWebpackPlugin.options.date %>
    </body>
    </html>

  完成上面的代码工作后,运行 webapck命令,我们打开 dist 文件中的 index.html。 index.html 运行结果

  通过运行结果,我们可以发现在顶部输出了当前时间,也就是 HtmlWebpackPlugin传入的参数,实际上 HtmlWebpackPlugin中的参数都可以通过 htmlWebpackPlugin.options.参数名称输出,我就不一一列举。

入门到放弃node系列之控制台和终端输出二维码

$
0
0

前言

本文首发公众号 【一名打字员】

今天推荐一个比较简单又好玩的模块 qrcode-terminal,这是一个在控制端生成二维码的模块,在我们编写某些控制台应用的时候可以很方便的进行某些操作。

使用方法

const qrcode = require('qrcode-terminal')

qrcode.generate('来来来,大家一起入门到放弃!');

//我们也可以这样写
qrcode.generate('来来来,大家一起入门到放弃!', {
    small: false
})

我们可以通过传入一个字符串生成一个很普通的二维码。

image.png

结语

qrcode-terminal支持windows和linux机器,所以无论是终端还是dos窗口都能够使用,还在犹豫啥,赶紧动手玩玩看吧!


你不知道的前端算法之热力图的实现

$
0
0

本文作者:TalkingData 可视化工程师李凤禄

编辑:Aresn

inMap 是一款基于 canvas 的大数据可视化库,专注于大数据方向点线面的可视化效果展示。目前支持散点、围栏、热力、网格、聚合等方式;致力于让大数据可视化变得简单易用。

GitHub 地址:https://github.com/TalkingData/inmap (点个 Star 支持下作者吧!)

热力图这个名字听起来很高大上,其实等同于我们常说的密度图。

image

如图表示,红色区域表示分析要素的密度大,而蓝色区域表示分析要素的密度小。只要点密集,就会形成聚类区域。 看到这么炫的效果,是不是自己也很想实现一把?接下来手把手实现一个热力(带你装逼带你飞、 哈哈),郑重声明:下面代码片段均来自 inMap

准备数据

inMap 接收的是经纬度数据,需要把它映射到 canvas 的像素坐标,这就用到了墨卡托转换,墨卡托算法很复杂,以后我们会有单独的一篇文章来讲讲他的原理。经过转换,你得到的数据应该是这样的:

[
  {
    "lng": "116.395645",
    "lat": 39.929986,
    "count": 6,
    "pixel": { //像素坐标
      "x": 689,
      "y": 294
    }
  },
  {
    "lng": "121.487899",
    "lat": 31.249162,
    "count": 10,
    "pixel": { //像素坐标
      "x": 759,
      "y": 439
    }
  },
  ...
]

好了,我们得到转换后的像素坐标数据(x、y),就可以做下面的事情了。

创建 canvas 渐变填充

创建一个由黑到白的渐变圆

let gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba(0,0,0,1)');
gradient.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = gradient;
ctx.arc(x, y, radius, 0, Math.PI * 2, true);
  • createRadialGradient() 创建线性的渐变对象
  • addColorStop() 定义一个渐变的颜色带

效果如图: image那么问题就来了,如果每个数据权重值 count 不一样,我们该如何表示呢?

设置 globalAlpha

根据不同的count值设置不同的Alpha,假设最大的count的Alpha等于1,最小的count的Alpha为0,那么我根据count求出Alpha。

let alpha = (count - minValue) / (maxValue - minValue);

然后我们代码如下:

drawPoint(x, y, radius, alpha) {
    let ctx = this.ctx;
    ctx.globalAlpha = alpha; //设置 Alpha 透明度
    ctx.beginPath();
    let gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
    gradient.addColorStop(0, 'rgba(0,0,0,1)');
    gradient.addColorStop(1, 'rgba(0,0,0,0)');
    ctx.fillStyle = gradient;
    ctx.arc(x, y, radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fill();
}

效果跟上一个截图有很大区别,可以对比一下透明度的变化。 image(这么黑乎乎的一团,跟热力差距好大啊)

image

重置 canvas 画布颜色

  • getImageData() 复制画布上指定矩形的像素数据
  • putImageData() 将图像数据放回画布:

getImageData()返回的数据格式如下:

{
  "data": {
    "0": 0,   //R
    "1": 128, //G
    "2": 0,   //B
    "3": 255, //Aplah
    "4": 0, //R
    "5": 128, //G
    "6": 0,  //B
    "7": 255, //Aplah
    "8": 0,
    "9": 128,
    "10": 0,
    "11": 255,
    "12": 0,
    "13": 128,
    "14": 0,
    "15": 255,
    "16": 0,
    "17": 128,
    "18": 0,
    "19": 255,
    "20": 0,
    "21": 128,
    "22": 0
    ...

返回的数据是一维数组,每四个元素表示一个像素(rgba)值。

实现热力原理:读取每个像素的alpha值(透明度),做一个颜色映射。

代码如下:

let palette = this.getColorPaint(); //取色面板
let img = ctx.getImageData(0, 0, container.width, container.height);
    let imgData = img.data;
    let max_opacity = normal.maxOpacity * 255;
    let min_opacity = normal.minOpacity * 255;
    //权重区间
    let max_scope = (normal.maxScope > 1 ? 1 : normal.maxScope) * 255;
    let min_scope = (normal.minScope < 0 ? 0 : normal.minScope) * 255;
    let len = imgData.length;
    for (let i = 3; i < len; i += 4) {
        let alpha = imgData[i]; 
        let offset = alpha * 4;
        if (!offset) {
            continue;
        }
        //映射颜色
        imgData[i - 3] = palette[offset];
        imgData[i - 2] = palette[offset + 1];
        imgData[i - 1] = palette[offset + 2];

        // 范围区间
        if (imgData[i] > max_scope) {
            imgData[i] = 0;
        }
        if (imgData[i] < min_scope) {
            imgData[i] = 0;
        }

        // 透明度
        if (imgData[i] > max_opacity) {
            imgData[i] = max_opacity;
        }
        if (imgData[i] < min_opacity) {
            imgData[i] = min_opacity;
        }
    }
    //将设置后的像素数据放回画布
ctx.putImageData(img, 0, 0, 0, 0, container.width, container.height);

创建颜色映射,一个好的颜色映射决定最终效果。 inMap 创建一个长256px的调色面板:

let paletteCanvas = document.createElement('canvas');
let paletteCtx = paletteCanvas.getContext('2d');
paletteCanvas.width = 256;
paletteCanvas.height = 1;
let gradient = paletteCtx.createLinearGradient(0, 0, 256, 1);

inMap 默认颜色如下:

this.gradient = {
    0.25: 'rgb(0,0,255)',
    0.55: 'rgb(0,255,0)',
    0.85: 'yellow',
    1.0: 'rgb(255,0,0)'
};

将gradient颜色设置到调色面板对象中

for (let key in gradient) {
    gradient.addColorStop(key, gradientConfig[key]);
}

返回调色面板的像素点数据:

return paletteCtx.getImageData(0, 0, 256, 1).data;

创建出来的调色面板效果图如下:(看起来像一个渐变颜色条)

image

最终我们实现的热力图如下:

image

下节预告

下一节,我们将重点介绍 inMap 文字避让算法的实现。

[杭州]前端架构师 高级前端工程师 - 知名外资软件公司

$
0
0

公司:外资上市近 30 年历史(业务稳定成熟,Facebook,ebay,80%知名的商业银行都是其客户)- 微策略 公司生活和工作能平衡、气氛简单直接,各种福利(健身、商业保险、各种培训兴趣班,美国工作的机会)。

感兴趣的朋友,请直接发英文简历请发: hr@adriano.com.cn( 1 个工作日内必会) 注意: 职位有效期 2018 年 12 月,不缺 HC,只缺技术高手,薪资可议。

目前前端人才 缺各个级别 包括架构师(带团队), 高级前端工程师。具体 JD 如下。 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、 职位: 前端架构师

岗位职责: 1、负责公司产品前端架构的建立和实施; 2、主导前端框架开发,制定前端开发规范; 3、负责梳理和优化前端开发、集成和部署流程,搭建高效集成的前端开发环境; 4、负责前端方案设计和技术选型; 5、有效地解决浏览器的兼容性,持续的优化前端体验和页面响应速度,并保证兼容性和执行率; 6、指导前端研发人员实际的开发工作,帮助解决中遇到的问题。

任职要求: 1、计算机相关专业本科(全日制统招)及以上学历,五年以上前端开发经验,两年以上架构级前端开发经历; 2、基本功扎实,熟悉 W3C 标准,精通前端开发技术(HTML5、JS、Ajax、Json、XHTML、CSS3),了解各项技术的相关标准,对 HTML、CSS、JavaScript 有深刻理解,对 HTTP 协议、浏览器内核有深刻理解; 3、熟悉至少一种前端框架:React、Angular、Vue、jquery 等,熟悉至少一种前端构建工具:Gulp、Grunt、fis3、webpack 等, 熟练 SASS、LESS 等预处理工具的使用; 4、具备对系统优化重构的能力; 5、具有极强的团队协作精神,具有团队培训发展规划能力; 6、英文流利。

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、 职位: 高级前端工程师 职责描述: 1、负责 web 网站的前端开发; 2、配合后台开发人员实现界面和功能; 3、拥有良好的代码习惯,要求结构清晰,命名规范,逻辑性强,代码冗余率低; 4、根据需要不断修改完善代码功能。

任职要求: 1、本科及以上学历,计算机相关专业,有三年及以上 Web 前端开发经验; 2、深刻理解 Web 标准,对可用性、易用性、可访问性等相关知识有实际了解和实践经验; 3、熟悉 JavaScript、HTML、CSS、Ajax 等开发技术,擅长 jQuery 等 JS 框架,并能熟练编写高性能的页面控件; 4、精通 HTML 代码,WEB 标准,有解决主流浏览器系列兼容性的能力与经验; 5、英文可以基本沟通 6、具有良好的沟通及团队协作能力。

nvm for windows切换node版本无效

$
0
0

使用 https://github.com/coreybutler/nvm-windows切换node版本,公司的电脑windows7系统不行,家里的windows10可以。

具体表现为: 如果全局安装了node,无论如何切换,运行的还是全局安装的node版本; 卸载全局node,使用nvm进行管理,nvm命令可以运行,但是提示找不到node和npm命令。 无论如何卸载重装删除npm文件夹都不行。

请问有没有在windows下使用nvm经验的兄弟,这个问题有没有解决方案?或者有没有替代方案,试了nvs安装不了。linux、mac就不要说了。 image.png

Vue 单页测试帖

express 里面如果想要用async/await,是用现在node里面的原生的async好还是用npm上面第三方的async库?

$
0
0

express 里面如果想要用async/await,是用现在node里面的原生的async好还是用npm上面第三方的async库?因为想试试在node里面使用异步来写同步

node怎么打印完整的函数?

$
0
0

node打印对象的方法有console, util.inspect, JSON.stringify,而且可以完整的打印;

JSON.stringify(obj, null, 1);  //JSON.stringify(value[, replacer[, space]])

第二参数用null,不用替换,第三个参数格式打印缩进或替代缩进的方式

但是,有没有可以完整打印函数的方法?比如很简单一个函数:

let a = function () {
    let b = 1;
    let c = 0;
    return b + c;
}

我想完整打印出来里面,显示代码

用console.log() 或者util.inspect()打印: [Function: a] 用JSON.stringif()打印是 undefined

获取不到req.body中携带的参数

$
0
0

最近才开始学习node.js,照着node-club的源码自己搭建了一个项目,但是到了表单提交这一块遇到了问题,body-parser用的自己安装的,也进行了相关配置,f12控制台也能看到表单提交的信息,就是始终拿不到req.body中的参数,求大神解答WX20171226-180259.pngWX20171226-180341.pngWX20171226-180831.pngWX20171226-180848.pngimage.png


eggjs的二级restful路由怎么定义?

$
0
0

比如,我定义user和device的restful路由会书写如下:

router.resources('users', '/api/v1/users', controller.v1.users);
router.resources('devices', '/api/v1/devices', controller.v1.devices);

但是对于 “查询指定用户下的所有设备” 这个接口,应该在rest里面怎么定义呢?

传统的都是: 查询指定用户下的所有设备:/api/v1/users/:id/devices

就是不知道eggjs里面怎么定义这种接口????

npm update 机制有没有比较好的插件推荐?

扒一扒那些年我们遇到的奇葩代码

$
0
0

前言

本文首发于公众号【一名打字员】

在工作中,我们通常会遇到传说中的“祖传”代码,有些是否让我们感到哭笑不得,今天本猿整理一下自己以及网络上出现过的奇葩代码。来源于互联网的均会标明出处。

TOP1

当仁不让的当然是传说中的“睡排序”,/手动捂脸

#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
 
int main(int argc, char* argv[]) {
  std::vector<std::thread> threads;
 
  for (int i = 1; i < argc; ++i) {
    threads.emplace_back([i, &argv]() {
      int arg = std::stoi(argv[i]);
      std::this_thread::sleep_for(std::chrono::seconds(arg));
      std::cout << argv[i] << std::endl;
    });
  }
 
  for (auto& thread : threads) {
    thread.join();
  }
}

当然他输出的结果是

./a.out 8 15 14 9 17 20 16 24 6 24 21 23 19 23 19 
6
8
9
14
15
16
17
19
19
20
21
23
23
24
24 

本猿觉得,其实除了名称奇葩以外,其它还是很正常的。(逃 from WIKI

TOP2

拼写容错,在知乎上看到的,23333…

#define ture true
#define flase false
#define viod void

from B乎

TOP3

变量命名是个体力活,常年混迹GITHUB(复制粘贴代码)的本猿,看过太多各种各样的命名了。我发现一个规律,小公司或者外包公司的打字员们,通常因为进度问题(当然也有可能有其它原因,我不管我不管),在一个函数方法内部会出现这样的命名。int a=0,aa=0,aaa=0,b=0,bb=0,bbb=0,c=0,cc=0,ccc=0,还有这样的

enum color{
     black,//黑色
     hong //红色   
    }

这位打字员,你是不知道红色的英文是啥吗还是要搞事情啊…

TOP4

是时候祭出当年我为代码开光的注释了:


                            _ooOoo_
                           o8888888o
                           88" . "88
                           (| -_- |)
                            O\ = /O
                        ____/`---'\____
                      .   ' \\| |// `.
                       / \\||| : |||// \
                     / _||||| -:- |||||- \
                       | | \\\ - /// | |
                     | \_| ''\---/'' | |
                      \ .-\__ `-` ___/-. /
                   ___`. .' /--.--\ `. . __
                ."" '< `.___\_<|>_/___.' >'"".
               | | : `- \`.;`\ _ /`;.`/ - ` : | |
                 \ \ `-. \_ __\ /__ _/ .-` / /
         ======`-.____`-.___\_____/___.-`____.-'======
                            `=---='

         .............................................
                  佛祖保佑             永无BUG

说起来,早些年我的代码里还是有很多这种注释的。2333 移步GITHUB

TOP5

在这里推荐一个网站,上面有很多这种好玩的东西,比如说下面这种

image.png

from xkcd

结语

大家平常有遇到啥好玩的东西或者是想要完成什么功能都可以发给【一名打字员】,本猿一定让你很满意的。最后希望大家都能写出没有bug的代码,一次编译一次通过。(逃

React中全局的事件通信有什么缺点?有什么坑?

$
0
0

最近在搞React及React Native,基本上用的就是mobx那套。 但是有些地方我想着可能用全局事件通知这样,一个组件通知,其它组件收到通知就去做一些事。 但是一想这样肯定不好,但是具体会有什么坑呢、

electron串口工具桌面应用开发踩坑记(node-serialport)

$
0
0

简介

这是一个基于electron的串口工具桌面应用,因为我在网上走了很多坑,都不成功,终于找到对的方法,编译成功了,这里讲下我的编译成功的方法。希望能够帮到大家

完整demo在demo目录中,包括编译好的node_modules,所以有点大

github地址: https://github.com/PowerDos/serialport_electron_start

实现过程

装备工作

安装python 2.7

这里需要安装python 2.7的环境,记得是2.7,3的话是不行的。这里就不再展开python的安装过程,执行下载安装即可。

安装electron

对于可以翻墙的同学用这个

npm i electron -g

由于下载过慢,所以我采用淘宝的镜像镜像安装cnpm,大家可以自行安装下cnpm,这里就不展开解释了 下面的操作都采用cnpm

cnpm i electron -g

安装electron-prebuilt

cnpm install -g electron-prebuilt

开始

安装官方的例子

官方文档的例子我就不在这里展开解释了,可以查看官方文档 https://electron.org.cn/doc/tutorial/quick-start.html

下载demo

git clone https://github.com/electron/electron-quick-start.git

安装模块

cnpm install

安装serialport

cnpm install --save serialport

安装electron-rebuild, 因为serialport是根据系统环境编译的,当我们安装时,我们编译的成的是系统环境的serialport,所以我们需要重新编译成eletron的环境,所以我们需要electron-rebuild

cnpm install --save-dev electron-rebuild

重新编译, 因为我当前版本的electron是1.7.10的,所以我们重新把模块编译成适应1.7.10的, 这里记得要根据你的electron版本编译

./node_modules/.bin/electron-rebuild -v 1.7.10

demo

在index.html写我们的demo

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    <!-- All of the Node.js APIs are available in this renderer process. -->
    We are using Node.js <script>document.write(process.versions.node)</script>,
    Chromium <script>document.write(process.versions.chrome)</script>,
    and Electron <script>document.write(process.versions.electron)</script>.

    <script>
      // You can also require other files to run in this process
      require('./renderer.js')
      var serialport = require('serialport');

      serialport.list(function(err, ports) {
        console.log(ports);
      });
    </script>
  </body>
</html>

运行效果

有什么需要改进的,可以直接提出来

Viewing all 14821 articles
Browse latest View live