修改最新版本Mysql默认密码(v5.7.10)

MySql版本: v5.7.10

Step1

安装mysql,加入PATH。

1
> vim ~/.zshrc

修改如下内容:注意路径中间用:隔开 export PATH="/usr/local/other_path:/usr/local/mysql/bin/:$PATH"

Step2

在系统偏好,最下面的mysql中点击Stop MySql Server按钮,关闭mysql后台进程

Step3

跳过mysql权限认证机制

1
> sudo mysqld_safe --user=mysql --skip-grant-tables --skip-networking

启动了mysql的安全模式,可以打开新的命令行窗口进入mysql

Step4

登录mysql,修改密码

1
2
3
4
5
> mysql
> use user;
> update user set authentication_string=password('123456') where user='root';
> flush privileges;
> exit

注:mysql5.7以后用户的password字段名变成了authentication_string,这点很坑。

Step5

关闭相关进程,重新登陆mysql

1
2
3
4
5
6
> ps -A | grep mysql
2004 ttys000 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn mysql
1733 ttys001 0:00.02 sudo mysqld_safe --user=mysql --skip-grant-tables --skip-networking
1735 ttys001 0:00.02 /bin/sh /usr/local/mysql/bin//mysqld_safe --user=mysql --skip-grant-tables --skip-networking
1846 ttys001 0:00.37 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --skip-grant-tables --skip-networking --log-error=/usr/local/mysql/data/wangchaoyouzancomdeMacBook-Pro.local.err --pid-file=/usr/local/mysql/data/wangchaoyouzancomdeMacBook-Pro.local.pid
> sudo kill -9 2004 1733 1735 1846

重新在系统偏好的Mysql中启动mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> mysql -u root -p
Enter password:
123456

Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 39
Server version: 5.7.10

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Step6

客户端用root用户登录失败提示如下内容:

Your password has expired. To log in you must change it using a client that supports expired passwords. -- Navicat Premium for MySql

You must reset your password using ALTER USER statement before executing this statement. -- MySql Workbentch

其中workbentch会自动跳出新窗口让你重置密码。 Navicat不会弹出窗口就用命令行的方式重置:

1
2
3
4
5
6
> mysql -u root -p
Enter password:


mysql> alter user 'root'@'localhost' identified by 'admin' password expire never;
mysql> exit

官方Alter User文档

密码的过期策略有四种选项

  1. EXPIRE 过期
  2. EXPIRE DEFAULT 过期
  3. EXPIRE NEVER 永不过期
  4. EXPIRE INTERVAL N DAY n天后过期

1
2
3
4
5
6
password_option: {
PASSWORD EXPIRE
| PASSWORD EXPIRE DEFAULT
| PASSWORD EXPIRE NEVER
| PASSWORD EXPIRE INTERVAL N DAY
}

在navicat中修改连接密码,即可连接成功。

如何开发Sublime插件

Sublime自带配置功能

.sublime-settings

JSON格式,供插件或sublime调用的配置文件。例如Preferences.sublime-settings(sublime的全局配置),WakaTime.sublime-settings(插件的配置和信息,例如api_key)

1
2
3
4
5
6
7
8
9
10
11
{
"color_scheme": "Packages/Devastate/Devastate.tmTheme",
"font_size": 12,
"ignored_packages":
[
"Vintage"
],
"theme": "Centurion.sublime-theme",
"word_wrap": true,
"wrap_width": 120
}

1
2
3
{
"api_key": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

.sublime-completions

输入自动提示,自动补全功能 插件实例:Bootstrap 3 Snippets, Javascript Completions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"scope": "source.js",

"completions":
[
{
"trigger": "new Date()\tDate",
"contents": "new Date()"
},
{
"trigger": "length\tDate",
"contents": "length"
},
{
"trigger": "valueOf()\tDate",
"contents": "valueOf()"
}
]
}

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"scope": "source.js",
"completions": [
{
"trigger": "description-Date",
"contents": "/*\n\tDescription:\n\tCreates a JavaScript Date instance that represents a single moment in time. Date objects are based on a time value that is the number of milliseconds since 1 January, 1970 UTC.\n\n\tSyntax:\n\tnew Date();\n\tnew Date(value);\n\tnew Date(dateString);\n\tnew Date(year, month, day, hour, minute, second, millisecond);\n*/"
},
{
"trigger": "description-Date.now()",
"contents": "/*\n\tDescription:\n\tThe Date.now() method returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.\n\n\tSyntax:\n\tvar timeInMs = Date.now();\n*/"
}
]
}

.sublime-snippets

代码自动补全,但是没有提示框。文件支持XML的CDATA格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<snippet>
<content><![CDATA[
<form action="${1}" method="${2:POST}" class="form-inline" role="form">

${3:<div class="form-group">
<label class="sr-only" for="">label</label>
<input type="email" class="form-control" id="" placeholder="Input field">
</div>}

${0}

<button type="submit" class="btn btn-primary">${4:Submit}</button>
</form>
]]></content>

<tabTrigger>bs3-form:inline</tabTrigger>
</snippet>

注:.sublime-completions文件和.sublime-snippets文件都不支持生成动态内容。比如在我的snippets插入当前时间。如果想要插入动态内容请使用这个插件:Smart Snippets

.sublime-commands

声明commands,一个command对应一个python class args对应这个class中run方法的参数

这个command可以在.sublime-keymap中引用,来定义快捷键 也可以直接在sublime的console中执行 control + ` view.run_command('')

Sublime提供了三种类型的command

  • Text Commands提供了对当前View对象(就是正在编辑的文件)内容的访问。
  • Window Commands提供里对当前编辑器Window对象的引用。
  • Application Commands不提供对任何window或者文件的引用,而且也很少用到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
{
"caption": "Emmet: Expand Abbreviation",
"command": "run_emmet_action",
"args": {
"action": "expand_abbreviation"
}
},

{
"caption": "Emmet: Wrap With Abbreviation",
"command": "wrap_as_you_type"
},

{
"caption": "Emmet: Balance (outward)",
"command": "run_emmet_action",
"args": {
"action": "balance_outward"
}
}
]

.sublime-keymap

sublime快捷键设置。例如Default (OSX).sublime-keymap

1
2
3
[
{ "keys": ["f5"], "command": "add_time" }
]

.sublime-menu

自定义菜单

  • Main.sublime-menu 顶部菜单
  • Side Bar.sublime-menu 右键操作左侧Side Bar菜单
  • Context.sublime-menu 右键操作文件菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[
{
"caption": "Preferences",
"mnemonic": "n",
"id": "preferences",
"children":
[
{
"caption": "WakaTime",
"mnemonic": "W",
"id": "wakatime-settings",
"children":
[
{
"command": "open_file", "args":
{
"file": "${packages}/WakaTime/WakaTime.sublime-settings"
},
"caption": "Settings – Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/WakaTime.sublime-settings"
},
"caption": "Settings – User"
}
]
}
]
}
]

Sublime插件开发

Examples

  • delete_word.py Deletes a word to the left or right of the cursor
  • duplicate_line.py Duplicates the current line
  • goto_line.py Prompts the user for input, then updates the selection
  • font.py Shows how to work with settings
  • mark.py Uses add_regions() to add an icon to the gutter
  • trim_trailing_whitespace.py Modifies a buffer just before its saved

delete_word.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import sublime, sublime_plugin

def clamp(xmin, x, xmax):
if x < xmin:
return xmin
if x > xmax:
return xmax
return x;

class DeleteWordCommand(sublime_plugin.TextCommand):
def find_by_class(self, pt, classes, forward):
if forward:
delta = 1
end_position = self.view.size()
if pt > end_position:
pt = end_position
else:
delta = -1
end_position = 0
if pt < end_position:
pt = end_position

while pt != end_position:
if self.view.classify(pt) & classes != 0:
return pt
pt += delta

return pt

def expand_word(self, view, pos, classes, forward):
if forward:
delta = 1
else:
delta = -1
ws = ["\t", " "]

if forward:
if view.substr(pos) in ws and view.substr(pos + 1) in ws:
classes = sublime.CLASS_WORD_START | sublime.CLASS_PUNCTUATION_START | sublime.CLASS_LINE_END
else:
if view.substr(pos - 1) in ws and view.substr(pos - 2) in ws:
classes = sublime.CLASS_WORD_END | sublime.CLASS_PUNCTUATION_END | sublime.CLASS_LINE_START

return sublime.Region(pos, self.find_by_class(pos + delta, classes, forward))

def run(self, edit, forward = True, sub_words = False):
if forward:
classes = sublime.CLASS_WORD_END | sublime.CLASS_PUNCTUATION_END | sublime.CLASS_LINE_START
if sub_words:
classes |= sublime.CLASS_SUB_WORD_END
else:
classes = sublime.CLASS_WORD_START | sublime.CLASS_PUNCTUATION_START | sublime.CLASS_LINE_END | sublime.CLASS_LINE_START
if sub_words:
classes |= sublime.CLASS_SUB_WORD_START

new_sels = []
for s in reversed(self.view.sel()):
if s.empty():
new_sels.append(self.expand_word(self.view, s.b, classes, forward))

sz = self.view.size()
for s in new_sels:
self.view.sel().add(sublime.Region(clamp(0, s.a, sz),
clamp(0, s.b, sz)))

self.view.run_command("add_to_kill_ring", {"forward": forward})

if forward:
self.view.run_command('right_delete')
else:
self.view.run_command('left_delete')

duplicate_line.py

1
2
3
4
5
6
7
8
9
10
11
import sublime, sublime_plugin

class DuplicateLineCommand(sublime_plugin.TextCommand):
def run(self, edit):
for region in self.view.sel():
if region.empty():
line = self.view.line(region)
line_contents = self.view.substr(line) + '\n'
self.view.insert(edit, line.begin(), line_contents)
else:
self.view.insert(edit, region.begin(), self.view.substr(region))

goto_line.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import sublime, sublime_plugin

class PromptGotoLineCommand(sublime_plugin.WindowCommand):

def run(self):
self.window.show_input_panel("Goto Line:", "", self.on_done, None, None)
pass

def on_done(self, text):
try:
line = int(text)
if self.window.active_view():
self.window.active_view().run_command("goto_line", {"line": line} )
except ValueError:
pass

class GotoLineCommand(sublime_plugin.TextCommand):

def run(self, edit, line):
# Convert from 1 based to a 0 based line number
line = int(line) - 1

# Negative line numbers count from the end of the buffer
if line < 0:
lines, _ = self.view.rowcol(self.view.size())
line = lines + line + 1

pt = self.view.text_point(line, 0)

self.view.sel().clear()
self.view.sel().add(sublime.Region(pt))

self.view.show(pt)

font.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import sublime, sublime_plugin

class IncreaseFontSizeCommand(sublime_plugin.ApplicationCommand):
def run(self):
s = sublime.load_settings("Preferences.sublime-settings")
current = s.get("font_size", 10)

if current >= 36:
current += 4
elif current >= 24:
current += 2
else:
current += 1

if current > 128:
current = 128
s.set("font_size", current)

sublime.save_settings("Preferences.sublime-settings")

class DecreaseFontSizeCommand(sublime_plugin.ApplicationCommand):
def run(self):
s = sublime.load_settings("Preferences.sublime-settings")
current = s.get("font_size", 10)
# current -= 1

if current >= 40:
current -= 4
elif current >= 26:
current -= 2
else:
current -= 1

if current < 8:
current = 8
s.set("font_size", current)

sublime.save_settings("Preferences.sublime-settings")

class ResetFontSizeCommand(sublime_plugin.ApplicationCommand):
def run(self):
s = sublime.load_settings("Preferences.sublime-settings")
s.erase("font_size")

sublime.save_settings("Preferences.sublime-settings")

mark.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import sublime, sublime_plugin

class SetMarkCommand(sublime_plugin.TextCommand):
def run(self, edit):
mark = [s for s in self.view.sel()]
self.view.add_regions("mark", mark, "mark", "dot",
sublime.HIDDEN | sublime.PERSISTENT)

class SwapWithMarkCommand(sublime_plugin.TextCommand):
def run(self, edit):
old_mark = self.view.get_regions("mark")

mark = [s for s in self.view.sel()]
self.view.add_regions("mark", mark, "mark", "dot",
sublime.HIDDEN | sublime.PERSISTENT)

if len(old_mark):
self.view.sel().clear()
for r in old_mark:
self.view.sel().add(r)

class SelectToMarkCommand(sublime_plugin.TextCommand):
def run(self, edit):
mark = self.view.get_regions("mark")

num = min(len(mark), len(self.view.sel()))

regions = []
for i in range(num):
regions.append(self.view.sel()[i].cover(mark[i]))

for i in range(num, len(self.view.sel())):
regions.append(self.view.sel()[i])

self.view.sel().clear()
for r in regions:
self.view.sel().add(r)

class DeleteToMark(sublime_plugin.TextCommand):
def run(self, edit):
self.view.run_command("select_to_mark")
self.view.run_command("add_to_kill_ring", {"forward": False})
self.view.run_command("left_delete")

trim_trailing_whitespace.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sublime, sublime_plugin

class TrimTrailingWhiteSpaceCommand(sublime_plugin.TextCommand):
def run(self, edit):
trailing_white_space = self.view.find_all("[\t ]+$")
trailing_white_space.reverse()
for r in trailing_white_space:
self.view.erase(edit, r)

class TrimTrailingWhiteSpace(sublime_plugin.EventListener):
def on_pre_save(self, view):
if view.settings().get("trim_trailing_white_space_on_save") == True:
view.run_command("trim_trailing_white_space")

class EnsureNewlineAtEofCommand(sublime_plugin.TextCommand):
def run(self, edit):
if self.view.size() > 0 and self.view.substr(self.view.size() - 1) != '\n':
self.view.insert(edit, self.view.size(), "\n")

class EnsureNewlineAtEof(sublime_plugin.EventListener):
def on_pre_save(self, view):
if view.settings().get("ensure_newline_at_eof_on_save") == True:
if view.size() > 0 and view.substr(view.size() - 1) != '\n':
view.run_command("ensure_newline_at_eof")

第三方Javascript支持

SublimeJs

插件提交到Package Control

  1. 插件的代码需要托管到Github/Bitbucket上,或者支持ssl的服务器。一下我们默认托管到了Github上。
  2. 需要有一个Tag,这个tag要符合semantic规范。为了显得我们的专业比较成熟,我没有使用v0.0.1而是v1.0.0。
  3. 需要fork一份sublime的官方代码wbond/package_control_channel
  4. 修改里边的一个json文件,把我们的插件信息写进去。注意:用tab而不是空格缩紧,否则会不通过。
  5. 提交一个pull-request。
  6. 等待通过审核。

Links

对于javascript关键字this的理解

今天上班时候突然接到了面试电话,然后在环境很不好的情况下被问到了this问题,之前以为自己理解的还可以,但是当面对真正的考验的时候还是表现的不尽如人意。于是今天下班回家,痛定思痛,重新复习一下javascript中非常重要的基础概念——关键字this

What is this?

简单说this就是函数当前的运行环境,但是难点就在于运行环境是可以变的。 例如下面一个例题:

1
2
3
4
5
6
7
8
9
10
11
12
var name = "Bob";  
var nameObj ={
name : "Tom",
showName : function(){
alert(this.name);
},
waitShowName : function(){
setTimeout(this.showName, 1000);
}
};

nameObj.waitShowName();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var subway={
name:'1号线',
speed:0,
run:function(speed){
this.speed=speed; //绑定到对象本身
function test(speed){
this.speed=speed+50;//竟然绑定到全局变量了,真是匪夷所思啊
}
test(speed);
}
};
subway.run(100);
console.log(subway.speed);//100
console.log(speed);//150

先把这两道题放着,最后再来分析。

In JavaScript, as in most object-oriented programming languages, this is a special keyword that is used within methods to refer to the object on which a method is being invoked. The this keyword is relative to the execution context, not the declaration context.

this指向函数执行时的当前对象(或理解为调用函数的对象)。该关键字在Javascript中和执行环境,而非声明环境有关。

this的定义相当简单,但是我们要知道的是如何确定是那个对象调用了函数,在不同的运行环境下,this到底指向谁?

Where is this?

Understanding JavaScript’s this keyword

通过Twitter的Angus Croll的博客,我们来梳理下this在不同上下文中的指向。

1. Global context

this指向全局对象(浏览器是window,nodejs是global

1
alert(this); //window

2. Function context

至少有4种情况通过函数应用改变this的指向

a) 作为方法调用

方法和函数的区别就是,前者能找到一个明确的归属(对象),而后者是全局的 this指向调用者

1
2
3
4
5
6
7
8
9
10
11
12
var a = {
b: function() {
return this;
}
};

a.b(); //a;
a['b'](); //a;

var c = {};
c.d = a.b;
c.d(); //c

b) 作为没有归属的方法调用

this is the global object (or `undefined` in strict mode)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var a = {
b: function() {
return this;
}
};

var foo = a.b;
foo(); //window

var a = {
b: function() {
var c = function() {
return this;
};
return c();
}
};

a.b(); //window

a.b()的例子不太好理解,因为当我们执行a.b()的时候,返回内容是c方法的执行结果,但是c并不是谁的属性,他是没有归属的,因此c的this表示全局环境。

1
2
3
4
5
6
7
var a = {
b: function() {
return (function() {return this;})();
}
};

a.b(); //window

自执行函数的调用者都是全局环境——window

c) 通过Function.prototype.call, Function.prototype.apply调用

call, apply函数可以将this指向第一个参数

1
2
3
4
5
6
7
8
9
10
var a = {
b: function(x1, x2, x3) {
return this;
}
};

var d = {};

a.b.call(d, x1, x2, x3); //d
a.b.apply(d, [x1, x2, x3]); //d

d) 通过new调用一个构造函数

this指向新建对象

1
2
3
4
5
var A = function() {
this.toString = function(){return "I'm an A"};
};

new A(); //"I'm an A"

3. Evaluation context

eval表达式中的this和eval执行环境下的this指向相同

1
2
3
4
5
6
7
8
9
alert(eval('this==window')); //true - (except firebug, see above)

var a = {
b: function() {
eval('alert(this==a)');
}
};

a.b(); //true;

总结

以上各种情况其实都满足this指向函数执行时的当前对象这句话。无论是全局环境还是对象内部调用,我们只要判断在程序执行上下文中,是谁让代码执行的。

比如,在Global context下,函数执行时没有明显的调用对象,那肯定就是window调用的,因为函数的执行离不开context,及一个调用者的作用域。

有些在语句块内执行的函数,虽然我们可以发现它存在于对象内部,看起来就是那个对象调用了它。但是,仔细分析,就发现自执行函数,和代码块内部定义了函数再执行,跟包裹它的对象毫无关系。最简单的方法就是,把这个可以的代码段从这个对象体内拿出来,如果他仍然能执行,证明他就是被global context调用的函数。

apply和call方法其实就是借用了第一个参数的执行环境,执行一个未被这个对象定义过的函数。仔细想想,就会发现,这两个方法就是干着借窝生蛋的勾当,而执行上下文仍然在那个窝里。

最后eval实际上和apply,call没什么两样,也是借窝生蛋,窝还是原来的窝,只不过功能更强大了,不仅可以生蛋,还可以做任何事情。

追赶时间的脚步

从出生到现在,除了时间,没有什么力量让我感到如此可怕,它可以创造一切,也可以毁灭一切。虽然,很多时候我们甚至可以肆意的说出 “我斗得过命运!” 这样逆天的话,但是在时间面前我们确是一粒尘埃,它老人家看过的东西太多,不会在乎我们的小性命。但在我们的维度里,时间又显得那样长,尤其当我们有所期待。渺小的性命也能爆发出磅礴的能量,一对反物质的湮灭,一次深情的对视。 ------题(che)记(dan)

最近很多时候苦恼办事效率低下,工作热情不高,执行力不足,这种情况很具有周期性。可能每个人都有这样的痛楚,明明自己有一颗想要努力奋斗的心,凡是身体却不听自己的使唤。我可以把这种毛病称为间歇性身心行动力失调,当然外在的因素起到关键的作用,比如:客户又提新需求,PMS里的bug数有增无减,团队内部负能量爆发,好看的女同事离职,这个月工资花光了等等。种种的负面影响加上长期加班引起的颈椎病复发,导致任何一个正常的人都无法承受这生命之重,虽然心里仍然鼓励自己坚持坚持,马上就要告别这个傻X的项目,做自己喜欢的事情这样看似很美好的借口,但是往往现实的残酷让你觉得,这TM要哪年才能上线呀。。当然这种外部因素一直以来是维持我们懒惰的好借口,但是对自我状况的科学调节才是解决问题正确的方法哟!

执行力

很多时候我们觉得自己想做一件事情,但最后没有做成,都会归咎于自己没时间,或者这事情我没做过,有好多东西需要学习,我没时间,这样看似蛮有道理的借口。但是事实是:我们觉得没时间以后,去刷朋友圈了,去约妹子看电影了,去打游戏了,去发呆了,去思考人生了,去刷知乎什么什么的了。由于以前不规律的生活习惯,导致我们在对新的计划作出成本估算是总会用以前的样本数据做一次毫无道理评估,而我们又满足于这次评估,因为没有对当下的我们造成任何损失。 所以,如果要提高执行力的第一步就是——抛弃过去不好的生活习惯,将你的新计划当成我这一生将要做的一个完全不一样的事情,这件事是我生命的一个新起点,我的生命将因此发光,人类将为我骄傲,宇宙也要为我让步,我,就是这么任性!

其次,当我们吃了熊心豹子胆想要完成一次自我的救赎时,往往第一步还迈出去就跌倒了。我就经常这样,比如某一次看了一段牛人的B-box视频,然后自己也模仿了一下,感觉自己好有天赋,于是下定决心要将这个本领学好,好多一些和小伙伴们交(zhuang)流(bi)的话题和资本。但是,当我兴高采烈的开始寻找一些网上分享的B-box速成指南后,发现这TM根本就不能速成吗,各种基本音要几千遍找到正确的发音方式,找到了还要几万遍的重复,让肌肉记忆下来,这还能不能愉快的和小伙伴们交流啦。结局就是,我仍然是一个正常的美声歌手。。 所以,很多时候我们光有热情去做某件事的时候,这种突如其来的热度是无法温暖我们将计划执行到底的,何况计划往往都还没有。因此,在想要做一件事之前,先让自己冷静下来,判断一下,我为什么要做这件事,这件事会对我现在或未来造成哪些影响,我能够为此付出多大的成本。这三个思考要在3分钟之内完成,因为思考久了就会发呆。。

当你在理智的状态下决定要去做某件事的以后,那接下来的就是——做计划了。

做计划

好的开始是成功的一半,这句话不假。但没有计划,你可能只会成功一半。往往事情的开始都会比我们预想的要顺利一些,因为我们还有热度。但是热度没了,我们就只能靠最开始的计划来让行动继续下去。很多人说,计划赶不上变化,索性就顺其自然。那就大错特错了,好的计划能让变化发生在可控制的范围内,比如,你想要学习一样东西,但是工作不得不让你加班,从起床开始加到睡觉,从周一开始加到周日,这可能就是你所谓的变化,但是这不是变化,而是中断,或者挂起。很多人碰到这种情况就会觉得自己要学习的原计划执行不下去了,因为当加班结束以后,你可能已经忘记了自己曾经立下豪言壮语,要学某个东西这件事了。但是,如果计划做的好,中断回复以后,你仍然可以按照之前的轨迹继续执行,好像中间的加班完全没有发生过一样。 如何做一个好的计划:计划的作用,是在自己迷失的时候帮助你找到方向。因此,可以多花一点时间做计划,学习前辈的经验,他们都跌倒了无数次,你知道他们在哪里跌倒就要尽量避免。计划不能过粗,也不能太细。思考计划的过程,应该自顶而下,从你要做的这件事开始,逐步分解,也就是分治策略;同时结合当前的状况做对比分析。

比如我想五年内,称为一名出色的架构师。如果要成为出色的架构师,应该先成为一家公司的核心技术力量,并且具有团队号召力,大局观和健康的身体。于是可以将目标分成两个分支,并按照倒推的方式,完成每年的里程碑目标,同时根据现在的情况判断计划完成需要的时间和可能性,然后微调。最终的计划可能是这样的(以下为不完全分析):

  • 第五年:我成为了架构师,团队凝聚力强,具有号召力,身体健康,充分了解市场和用户。
  • 第四年:我成为了公司的技术核心,显然成为技术核心是需要很多学习和项目经验的,但是我忽略了周围和同事之间的相处,好像需要提高一下凝聚力,那么就从沟通开始,希望每周和大家有空一起外出运行一下,唱唱歌撸撸串。但是技术也不能落下,架构设计方面的知识有待提高,多看看大型项目的开发经验,研究一些复杂系统的设计思路,当然身体还是要棒棒的!
  • 第三年:我的技术成长飞快,老板好像很喜欢我了,毕竟三年来成功完成了很多项目,也用业余时间学习了不少知识,和团队的关系也挺融洽的,毕竟大家都是程序员,人畜无害的生物嘛~但是很多时候自己研究还是不够的,也要为公司的未来考虑考虑,大家好才是真的好。以后经常和大家做一些技术分享,新来的员工也要知道我们这些老人走过的弯路,培训嘛,还是实在点好。市场这种东西,也应该多学一学,毕竟公司不只有一个开发部。
  • 第二年:来公司第二年了,已经可以独当一面了,毕竟第一年我的付出可是有目共睹的,然而距离架构师还有很长一段路要走,毕竟这是公司技术超牛的人呀。操作系统,数据库,算法,分布式,TCP/IP,设计模式,前后端常用的框架,我还有很多要学的,怎么可以松懈呢。
  • 第一年:毕业了好开心,但是工作比上学要累的多呢。还要做了我喜欢做的事,虽然项目事情很多,但是我年轻,我是小兵,我要干好我的工作,老板就会满意,剩下的时间多学点东西,都是为我以后成为架构师做的长远投资。看来娱乐的时间不能太多了呀,但是要多锻炼,身体要健健康康的。多向同事学习,多思考,经过自己的努力没法解决的事情要及时请教别人。总之,这一年要努力提高自己的技术,让大家觉得我是项目组的核心人物,哈哈,就这么办吧!加油!

反馈

计划并非是一成不变的,一个好的系统都会有自我调节能力,垃圾多了要回收,电源线拔了要进入省电模式,女朋友生气了要去哄。反馈是维持系统正常运转的必要内容,也是保证我们计划顺利进行的关键因素。上文中否定了计划赶不上变化这句话,但是真的遇到了变化应该如何处理。 原计划遇到变故,导致影响了正常的执行轨迹,如果不及时调整就会导致计划失控,最终目标无法达成。类似于破窗效应,有了破窗赶紧修。既然计划受到影响,那就及时调整计划,可以推迟计划的原定时间,也可以通过加快执行效率追上正常的计划预期。总之,在失控之前,修复它。

反馈,就是周期性的检查一下当前的状态是否支撑你按照原进度完成该做的事情。有点**“吾日三省吾身”**的感觉。

Canvas之四 康威生命游戏2号

本文可以自己创建一个符合康威游戏规则的小世界,怎么发挥都随便你,看着这个这些虚拟小生命的繁衍,体验做上帝的感觉。 <link rel="stylesheet" type="text/css" href="/css/canvas4.css"> <div style="display:inline-block;">第<span id="generation" style="width: 100px;"></span>代<button onclick="reset();">重置</button><button onclick="start();">开始</button><input id="speed" placeholder="100 ms/g" style="width: 80px"><button onclick="stop();">暂停</button><button onclick="random();">随机</button><button onclick="nextGeneration();">下一代</button></div><hr><div><div class="left"><table id="diyTable"></table></div><canvas id="myCanvas" width="440" height="440">您的浏览器不支持HTML5</canvas></div> <script> var ifInit = false; var canvas = document.getElementById("myCanvas"); var height = canvas.height, width = canvas.width, context = canvas.getContext("2d"); var tx = 40, ty = 40; // 记录长宽有多少个方块 var cellWidth = height / ty, cellHeight = width / tx; // 小细胞的长宽 var lastData = []; // 记录上一代的数据 var data = []; // (height+2) * (width + 2) var generation; var born = 3, // 当周围有三个活着的时候,这个位置会活一个 keep = 2; // 当周围有两个的时候,这个位置不变,其他情况都死 var running; // 是否运行 var intervalTime = 100; // 每次迭代的时间间隔 var generationText = document.getElementById("generation"); var diyTable = document.getElementById("diyTable");

// 初始化数据和canvas
function init() {
    generation = 0;
    running = false;
    intervalTime = parseInt(document.getElementById("speed").value);
    lastData = buildTwoDArray(ty + 2, tx + 2);
    data = buildTwoDArray(ty + 2, tx + 2);
    context.clearRect(0, 0, width, height);
    generationText.innerHTML = generation;
    ifInit ? 1 : initTable();
    clearTable();
}

// 随机生成数据
function random() {
	init();
    lastData = buildTwoDArray(ty + 2, tx + 2);
    data = buildTwoDArray(ty + 2, tx + 2);
    // 生成画面的内容,即data的数据
    for (var i = 0; i < ty; i++) {
        for (var j = 0; j < tx; j++) {
            if (Math.random() > 0.8) {
                data[i + 1][j + 1] = 1;
            }
        }
    }
    // 随机生成后要画出来
    paintTable();
    paint();
}

// 初始化选择变革
function initTable() {
    // 绘制表格
    var html = [];
    for (var i = 0; i < ty; i++) {
        html.push("<tr>");
        for (var j = 0; j < tx; j++) {
            html.push("<td onclick='selectCell(this, " + i + "," + j + ");'></td>")
        }
        html.push("</tr>");
    }
    diyTable.innerHTML = html.join("");
    ifInit = true;
}

function selectCell(cell, x, y) {
    if (data[x + 1][y + 1]) {
        // 取消选择
        cell.style.backgroundColor = "white";
        data[x + 1][y + 1] = 0;
    } else {
        // 选择
        cell.style.backgroundColor = "blue";
        data[x + 1][y + 1] = 1;
    }
}

// 清空选择表格
function clearTable() {
    // 恢复所有单元格的颜色
    for (var i = 0; i < ty; i++) {
        for (var j = 0; j < tx; j++) {
            diyTable.rows[i].cells[j].style.backgroundColor = "white";
        }
    }
    // 恢复数据
    lastData = buildTwoDArray(ty + 2, tx + 2);
    data = buildTwoDArray(ty + 2, tx + 2);
}

// 填充选择表格
function paintTable() {
    for (var i = 0; i < ty; i++) {
        for (var j = 0; j < tx; j++) {
            if (data[i + 1][j + 1]) {
                diyTable.rows[i].cells[j].style.backgroundColor = "blue";
            } else {
                diyTable.rows[i].cells[j].style.backgroundColor = "white";
            }
        }
    }
}

function drawCell(y, x) {
    var cx = x * cellWidth;
    var cy = y * cellHeight;
    context.fillStyle = "Gold";
    context.fillRect(cx, cy, cellWidth, cellHeight);
    context.strokeStyle = "DarkGoldenRod";
    context.strokeRect(cx + 1, cy + 1, cellWidth - 2, cellHeight - 2);
}

function killCell(y, x) {
    var cx = x * cellWidth;
    var cy = y * cellHeight;
    context.clearRect(cx, cy, cellWidth, cellWidth);
}

// 将数据画到canvas上
function paint() {
    for (var i = 0; i < ty; i++) {
        for (var j = 0; j < tx; j++) {
            if (data[i + 1][j + 1] && !lastData[i + 1][j + 1]) {
                // 有生命
                drawCell(i, j);
            } else if (!data[i + 1][j + 1] && lastData[i + 1][j + 1]) {
                // 没有生命
                killCell(i, j);
            }
        }
    }
    generationText.innerHTML = generation;
}

// 生成二维数组,所有初始化为0
function buildTwoDArray(x, y) {
    var arr = [];
    for (var i = 0; i < x; i++) {
        arr[i] = [];
        for (var j = 0; j < y; j++) {
            arr[i][j] = 0;
        }
    }
    return arr;
}

// 进行这一代的计算,并将上一代的数据保存到lastData中
function generate() {
    lastData = data;
    data = buildTwoDArray(ty + 2, tx + 2);
    // 重新生成新一代的数据
    for (var i = 1; i <= ty; i++) {
        for (var j = 1; j <= tx; j++) {
            // 周围有几个或者的
            var state = lastData[i - 1][j - 1] + lastData[i - 1][j] + lastData[i - 1][j + 1] +
                    lastData[i][j - 1] + lastData[i][j + 1] +
                    lastData[i + 1][j - 1] + lastData[i + 1][j] + lastData[i + 1][j + 1];
            if (state == born) {
                data[i][j] = 1;
            } else if (state == keep) {
                data[i][j] = lastData[i][j];
            } else {
                data[i][j] = 0;
            }
        }
    }
    generation++;
}

function start() {
    intervalTime = parseInt(document.getElementById("speed").value, 10);
    function run() {
        if (running) {
            setTimeout(function () {
                paint();
                generate();
                run();
            }, intervalTime);
        }
    }
    running = true;
    run();
}

function stop() {
    running = false;
}

function reset() {
    init();
}

function nextGeneration() {
    paint();
    generate();
}

window.addEventListener("load", init, true);

</script>

Canvas之三 康威生命游戏1号

本文实现了一个随机小世界的繁衍过程,可以控制迭代速度,纯原生js实现

<div style="display:inline-block;">第<span id="generation" style="width: 100px;"></span>代<button onclick="reset();">重置</button><button onclick="start();">开始</button><input id="speed" placeholder="100 ms/g" style="width: 80px"><button onclick="stop();">暂停</button><button onclick="nextGeneration();">下一代</button></div><hr><div><canvas id="myCanvas" width="400" height="400">您的浏览器不支持HTML5</canvas></div> <script> var canvas = document.getElementById("myCanvas"); var height = canvas.height, width = canvas.width, context = canvas.getContext("2d"); var lastData = []; // 记录上一代的数据 var data = []; // (height+2) * (width + 2) var imageData; var generation; var born = 3, // 当周围有三个活着的时候,这个位置会活一个 keep = 2; // 当周围有两个的时候,这个位置不变,其他情况都死 var running; // 是否运行 var intervalTime = 100; // 每次迭代的时间间隔

// 初始化数据和canvas
function init() {
    generation = 0;
    running = false;
    intervalTime = parseInt(document.getElementById("speed").value);
    lastData = buildTwoDArray(height + 2, width + 2);
    data = buildTwoDArray(height + 2, width + 2);
    context.clearRect(0, 0, width, height);
    imageData = context.getImageData(0, 0, width, height);
    document.getElementById("generation").innerHTML = generation.toString();
    random();
}

// 随机生成数据
function random() {
    // 生成画面的内容,即data的数据
    for (var i = 0; i < height; i++) {
        for (var j = 0; j < width; j++) {
            if (Math.random() > 0.8) {
                data[i + 1][j + 1] = 1;
            }
        }
    }
    // 随机生成后要画出来
    paint();
}

// 将数据花到canvas上
function paint() {
    var d = imageData.data;
    // 生成绘制到canvas上的,下面代码的操作对象是图像流(数组)
    for (var i = 0; i < height; i++) {
        for (var j = 0; j < width; j++) {
            if (data[i + 1][j + 1] && !lastData[i + 1][j + 1]) {
                var pos = (i * width + j) * 4;
                // 有生命,变成红色
                d[pos] = 255; // red
                d[pos + 1] = 0; // green
                d[pos + 2] = 0; // blue
                d[pos + 3] = 255;   // 阿尔法通道,这个不设置会变成全透明
            } else if (!data[i + 1][j + 1] && lastData[i + 1][j + 1]) {
                var pos = (i * width + j) * 4;
                // 有生命,变成红色
                d[pos] = 255; // red
                d[pos + 1] = 255; // green
                d[pos + 2] = 255; // blue
            }
        }
    }
    context.putImageData(imageData, 0, 0);
    document.getElementById("generation").innerHTML = generation.toString();
}

// 生成二维数组,所有初始化为0
function buildTwoDArray(x, y) {
    var arr = [];
    for (var i = 0; i < x; i++) {
        arr[i] = [];
        for (var j = 0; j < y; j++) {
            arr[i][j] = 0;
        }
    }
    return arr;
}

// 进行这一代的计算,并将上一代的数据保存到lastData中
function generate() {
    lastData = data;
    data = buildTwoDArray(height + 2, width + 2);
    // 重新生成新一代的数据
    for (var i = 1; i <= height; i++) {
        for (var j = 1; j <= width; j++) {
            // 周围有几个或者的
            var state = lastData[i - 1][j - 1] + lastData[i - 1][j] + lastData[i - 1][j + 1] +
                    lastData[i][j - 1] + lastData[i][j + 1] +
                    lastData[i + 1][j - 1] + lastData[i + 1][j] + lastData[i + 1][j + 1];
            if (state == born) {
                data[i][j] = 1;
            } else if (state == keep) {
                data[i][j] = lastData[i][j];
            } else {
                data[i][j] = 0;
            }
        }
    }
    generation++;
}

function start() {
    intervalTime = parseInt(document.getElementById("speed").value);
    function run() {
        if (running) {
            setTimeout(function () {
                paint();
                generate();
                run();
            }, intervalTime);
        }
    }
    running = true;
    run();
}

function stop() {
    running = false;
}

function reset() {
    init();
}

function nextGeneration() {
    paint();
    generate();
}
window.addEventListener("load", init, true);

</script>

Canvas之二 像素处理技法

继续上一篇文章,这篇文章介绍通过Canvas对图片(Image对象)的像素处理方法,同样文章参考阮一峰前辈的Canvas API文档

作者也曾经玩过一年多的Ps,但是一直没有深入了解过图片处理的计算方法,这里也顺便复习一下,一些概念知识片面的带过,有时间再写文章专门讨论。

正题

其实,HTML5中通过Canvas处理图像只需要了解两个函数就够了,getImageDataputImageData,前者是读取一个Canvas的内容,返回一个对象,我们取名imageDate,

1
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);

其中的data属性,其实是一个一维数组,每四个值为一组[data[4k], data[4k+3]],分别表示r,g,b光的三原色和Alpha通道(透明度),取值都是[0, 255]。

后者是一个写方法,可以将数组内容重新绘制到Canvas中。

1
context.putImageData(imageData, 0, 0);

我们以下面的一张图片为例:

Canvas Pic 1

首先,我们要把图片插入到Canvas中。

1
2
3
var img = new Image();
img.src = "/image/canvas-1.png";
context.drawImage(img, 0, 0); // 设置对应的图像对象,以及它在画布上的位置

由于图像的载入需要时间,drawImage方法只能在图像完全载入后才能调用,因此上面的代码需要改写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var image = new Image(); 

image.onload = function() {

if (image.width != canvas.width)
canvas.width = image.width;
if (image.height != canvas.height)
canvas.height = image.height;

context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);

}

image.src = "/image/canvas-1.png";

drawImage()方法接受三个参数,第一个参数是图像文件的DOM元素(即img标签),第二个和第三个参数是图像左上角在Canvas元素中的坐标,上例中的(0, 0)就表示将图像左上角放置在Canvas元素的左上角。

当我们把图片用画笔(context)绘制到画布上(Canvas),就可以用上边提到的getImageData方法获得图像的数据了。假设我们图像处理函数叫filter,那么整个处理过程如下:

1
2
3
4
5
if (canvas.width > 0 && canvas.height > 0) {
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
filter(imageData);
context.putImageData(imageData, 0, 0);
}

1. 灰度效果

数学原理

灰度图(grayscale)就是取红、绿、蓝三个像素值的算术平均值,这实际上将图像转成了黑白形式。假定d[i]是像素数组中一个象素的红色值,则d[i+1]为绿色值,d[i+2]为蓝色值,d[i+3]就是alpha通道值。转成灰度的算法,就是将红、绿、蓝三个值相加后除以3,再将结果写回数组。

代码

1
2
3
4
5
6
7
8
9
10
grayscale = function (pixels) {
var d = pixels.data;
for (var i = 0; i < d.length; i += 4) {
var r = d[i];
var g = d[i + 1];
var b = d[i + 2];
d[i] = d[i + 1] = d[i + 2] = (r+g+b)/3;
}
return pixels;
};

2. 复古效果

数学原理

复古效果(sepia)则是将红、绿、蓝三个像素,分别取这三个值的某种加权平均值,使得图像有一种古旧的效果。

代码

1
2
3
4
5
6
7
8
9
10
11
12
sepia = function (pixels) {
var d = pixels.data;
for (var i = 0; i < d.length; i += 4) {
var r = d[i];
var g = d[i + 1];
var b = d[i + 2];
d[i] = (r * 0.393)+(g * 0.769)+(b * 0.189); // red
d[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168); // green
d[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131); // blue
}
return pixels;
};

3. 红色蒙版效果

数学原理

红色蒙版指的是,让图像呈现一种偏红的效果。算法是将红色通道设为红、绿、蓝三个值的平均值,而将绿色通道和蓝色通道都设为0。

代码

1
2
3
4
5
6
7
8
9
10
11
red = function (pixels) {
var d = pixels.data;
for (var i = 0; i < d.length; i += 4) {
var r = d[i];
var g = d[i + 1];
var b = d[i + 2];
d[i] = (r+g+b)/3; // 红色通道取平均值
d[i + 1] = d[i + 2] = 0; // 绿色通道和蓝色通道都设为0
}
return pixels;
};

4. 亮度效果

数学原理

亮度效果(brightness)是指让图像变得更亮或更暗。算法将红色通道、绿色通道、蓝色通道,同时加上一个正值或负值。

代码

1
2
3
4
5
6
7
8
9
brightness = function (pixels, delta) {
var d = pixels.data;
for (var i = 0; i < d.length; i += 4) {
d[i] += delta; // red
d[i + 1] += delta; // green
d[i + 2] += delta; // blue
}
return pixels;
};

5. 反转效果

数学原理

反转效果(invert)是指图片呈现一种色彩颠倒的效果。算法为红、绿、蓝通道都取各自的相反值(255-原值)。

代码

1
2
3
4
5
6
7
8
9
invert = function (pixels) {
var d = pixels.data;
for (var i = 0; i < d.length; i += 4) {
d[i] = 255 - d[i];
d[i+1] = 255 - d[i + 1];
d[i+2] = 255 - d[i + 2];
}
return pixels;
};

实例: <div style="display: inline-block;"><button onclick="doBack();">复原</button><button onclick="doGrayscale();">灰度效果</button><button onclick="doSepia();">复古效果</button><button onclick="doRed();">红色蒙版效果</button><input text="text" placeholder="亮度参数 -255 ~ 255" id="delta"><button onclick="doBrightness();">亮度效果</button><button onclick="doInvert();">反转效果</button></div><canvas id="myCanvas" width="1300" height="400">您的浏览器不支持canvas!</canvas> <script type="text/javascript"> var canvas = document.getElementById('myCanvas'); var context = canvas.getContext('2d'); var image = new Image(); var originImageData; var imageData;

image.onload = function() {

if (image.width != canvas.width)
    canvas.width = image.width;
if (image.height != canvas.height)
    canvas.height = image.height;

context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
originImageData = context.getImageData(0, 0, canvas.width, canvas.height);

} image.src = "/image/canvas-1.png";

function doWhat(filter, delta) { if (canvas.width > 0 && canvas.height > 0) { imageData = context.getImageData(0, 0, canvas.width, canvas.height); delta ? filter(imageData, delta) : filter(imageData); context.putImageData(imageData, 0, 0); } }

function doBack() { if (canvas.width > 0 && canvas.height > 0) { context.putImageData(originImageData, 0, 0); } }

var grayscale = function (pixels) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { var r = d[i]; var g = d[i + 1]; var b = d[i + 2]; d[i] = d[i + 1] = d[i + 2] = (r+g+b)/3; } return pixels; };

var sepia = function (pixels) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { var r = d[i]; var g = d[i + 1]; var b = d[i + 2]; d[i] = (r * 0.393)+(g * 0.769)+(b * 0.189); // red d[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168); // green d[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131); // blue } return pixels; };

var red = function (pixels) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { var r = d[i]; var g = d[i + 1]; var b = d[i + 2]; d[i] = (r+g+b)/3; // 红色通道取平均值 d[i + 1] = d[i + 2] = 0; // 绿色通道和蓝色通道都设为0 } return pixels; };

var brightness = function (pixels, delta) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { d[i] += delta; // red d[i + 1] += delta; // green d[i + 2] += delta; // blue
} return pixels; };

var invert = function (pixels) { var d = pixels.data; for (var i = 0; i < d.length; i += 4) { d[i] = 255 - d[i]; d[i+1] = 255 - d[i + 1]; d[i+2] = 255 - d[i + 2]; } return pixels; };

function doGrayscale() { doWhat(grayscale); }

function doSepia() { doWhat(sepia); }

function doRed() { doWhat(red); }

function doBrightness() { var delta = parseInt(document.getElementById("delta").value, 10); doWhat(brightness, delta); }

function doInvert() { doWhat(invert); }

</script>

Canvas之一 旋转的太极

今天看到了康威生命游戏,有点小激动,想自己用javascript实现一个。本来想用表格的方式着色去做,但是又看到了已经有人通过HTML5的canvas完成了,代码都公布出来了HTML5版本的生命游戏,而且下下来就能运行。。然后就想着我可以做个更好的,但是不会canvas怎么办?学呗!经常知识栈溢出的我就是这么任性。

在寻找好的学习资料的道路上又遇到了阮一峰大神,他还是我的校友你敢信?真是学习的榜样啊!一个好的Canvas API学习文档

然后就这么折腾了一小会儿,写了个简单的旋转的太极图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<canvas id="myCanvas" width="400" height="400">
您的浏览器不支持canvas!
</canvas>
</body>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var height = 400;
var width = 400;
/*
ctx.beginPath(); // 开始路径绘制
ctx.moveTo(20, 20); // 设置路径起点,坐标为(20,20)
ctx.lineTo(200, 20); // 绘制一条到(200,20)的直线
ctx.lineTo(200, 100);
ctx.lineTo(20, 100);
ctx.closePath();
ctx.lineWidth = 1.0; // 设置线宽
ctx.strokeStyle = "#CC0000"; // 设置线的颜色
ctx.stroke(); // 进行线的着色,这时整条线才变得可见
ctx.fillStyle = 'yellow'; // 设置矩形的填充色
ctx.fillRect(50, 50, 200, 100); // 绘制一个有填充色的矩形
ctx.strokeRect(10, 10, 200, 100); // 透明矩形
ctx.clearRect(100, 50, 50, 50); // 清楚对应位置矩形区域
// 设置字体
ctx.font = "Bold 20px Arial";
// 设置对齐方式
ctx.textAlign = "left";
// 设置填充颜色
ctx.fillStyle = "#008600";
// 设置字体内容,以及在画布上的位置, 不支持文本断行
ctx.fillText("Hello!", 10, 50);
// 绘制空心字, 不支持文本断行
ctx.strokeText("Hello!", 10, 100);
// 清空画布
ctx.clearRect(0, 0, 400, 200);
// 绘制圆和扇形
ctx.beginPath();
ctx.arc(60, 60, 50, 0, Math.PI, true);
ctx.fillStyle = "#000000";
ctx.fill();
// 绘制空心圆
ctx.beginPath();
ctx.arc(60, 60, 50, 0, Math.PI * 2, true);
ctx.lineWidth = 1.0;
ctx.strokeStyle = "#000";
ctx.stroke();
// 根据上边画出来的,绘制一个太极图
ctx.beginPath();
ctx.arc(35, 60, 25, 0, Math.PI * 2, true);
ctx.fillStyle = "#ffffff";
ctx.fill();
ctx.beginPath();
ctx.arc(85, 60, 25, 0, Math.PI * 2, true);
ctx.fillStyle = "#000000";
ctx.fill();
ctx.beginPath();
ctx.arc(35, 60, 8, 0, Math.PI * 2, true);
ctx.fillStyle = "#000000";
ctx.fill();
ctx.beginPath();
ctx.arc(85, 60, 8, 0, Math.PI * 2, true);
ctx.fillStyle = "#ffffff";
ctx.fill();
// 设置渐变色
var myGradient = ctx.createLinearGradient(0, 0, 0, 160);
myGradient.addColorStop(0, "#BABABA");
myGradient.addColorStop(1, "#636363");
ctx.fillStyle = myGradient;
ctx.fillRect(10,10,200,100);
// 设置阴影
ctx.shadowOffsetX = 10; // 设置水平位移
ctx.shadowOffsetY = 10; // 设置垂直位移
ctx.shadowBlur = 5; // 设置模糊度
ctx.shadowColor = "rgba(0,0,0,0.5)"; // 设置阴影颜色
ctx.fillStyle = "#CC0000";
ctx.fillRect(10,10,200,100);
*/


// 设置坐标原点
ctx.translate(width / 2, height / 2);

// 做一个旋转的太极图
setInterval(function () {
// 清空画布
ctx.clearRect(-width / 2, height / 2, width / 2, -height / 2);
// 旋转画布
ctx.rotate(Math.PI / 20);
// 绘制圆的轮廓
ctx.beginPath();
ctx.arc(0, 0, 50, 0, Math.PI * 2, true);
ctx.lineWidth = 2.0;
ctx.strokeStyle = "#000";
ctx.stroke();
// 画个黑圆
ctx.beginPath();
ctx.arc(0, 0, 50, 0, Math.PI, true);
ctx.fillStyle = "#000000";
ctx.fill();
// 画个白圆
ctx.beginPath();
ctx.arc(0, 0, 50, Math.PI, Math.PI * 2, true);
ctx.fillStyle = "#ffffff";
ctx.fill();
// 根据上边画出来的,绘制一个太极图
ctx.beginPath();
ctx.arc(-25, 0, 25, 0, Math.PI * 2, true);
ctx.fillStyle = "#ffffff";
ctx.fill();
ctx.beginPath();
ctx.arc(25, 0, 25, 0, Math.PI * 2, true);
ctx.fillStyle = "#000000";
ctx.fill();
ctx.beginPath();
ctx.arc(-25, 0, 8, 0, Math.PI * 2, true);
ctx.fillStyle = "#000000";
ctx.fill();
ctx.beginPath();
ctx.arc(25, 0, 8, 0, Math.PI * 2, true);
ctx.fillStyle = "#ffffff";
ctx.fill();
}, 50);
</script>

</html>

如下为效果图: <canvas id="myCanvas" width="400" height="400"> 您的浏览器不支持canvas! </canvas> <script type="text/javascript"> var canvas = document.getElementById('myCanvas'); var ctx = canvas.getContext('2d'); var height = 400; var width = 400;

// 设置坐标原点
ctx.translate(width / 2, height / 2);

// 做一个旋转的太极图
setInterval(function () {
    // 清空画布
    ctx.clearRect(-width / 2, height / 2, width / 2, -height / 2);
    // 旋转画布
    ctx.rotate(Math.PI / 20);
    // 绘制圆的轮廓
    ctx.beginPath();
    ctx.arc(0, 0, 50, 0, Math.PI * 2, true);
    ctx.lineWidth = 2.0;
    ctx.strokeStyle = "#000";
    ctx.stroke();
    // 画个黑圆
    ctx.beginPath();
    ctx.arc(0, 0, 50, 0, Math.PI, true);
    ctx.fillStyle = "#000000";
    ctx.fill();
    // 画个白圆
    ctx.beginPath();
    ctx.arc(0, 0, 50, Math.PI, Math.PI * 2, true);
    ctx.fillStyle = "#ffffff";
    ctx.fill();
    // 根据上边画出来的,绘制一个太极图
    ctx.beginPath();
    ctx.arc(-25, 0, 25, 0, Math.PI * 2, true);
    ctx.fillStyle = "#ffffff";
    ctx.fill();
    ctx.beginPath();
    ctx.arc(25, 0, 25, 0, Math.PI * 2, true);
    ctx.fillStyle = "#000000";
    ctx.fill();
    ctx.beginPath();
    ctx.arc(-25, 0, 8, 0, Math.PI * 2, true);
    ctx.fillStyle = "#000000";
    ctx.fill();
    ctx.beginPath();
    ctx.arc(25, 0, 8, 0, Math.PI * 2, true);
    ctx.fillStyle = "#ffffff";
    ctx.fill();
}, 50);

</script>