Open hellowoody opened 4 years ago
前提:配置好简单的nodejs和bootstrap的服务,让用户可以通过localhost:8001和localhost:8001/index.html都可以访问
fs.readFile("."+request.url,'utf-8',function(err,data)
{
if(err)
{
throw err;
}
response.writeHead(200,{"Content-Type":{
".html":"text/type",
".css":"text/css",
".js" :"application/javascript"
}
});
response.write(data);
response.end();
})
火狐:两个入口都可以访问,样式也正常加载了。
ie:localhost:8001/index.html可以正常访问,localhost:8001是下载index.html页面
chrome:两个入口都可以访问,但样式加载不了
解决办法:
switch (ext)
{
case ".css":
case ".js":
fs.readFile("."+request.url,'utf-8',function(err,data){
if(err)
{
throw err;
}
response.writeHead(200,{"Content-Type":{
".css":"text/css",
".js" :"application/javascript"
}[ext]
});
response.write(data);
response.end();
})
break;
default :
fs.readFile("./index.html","utf-8",function(err,data)<br> {<br> if(err) throw err;<br>
第一步,下载分区助手,分出一个空白区来(注:以wubi这种方式装ubuntu最大为30G,所以分区时分10-30G之间即可)
第二步,从ubuntu官网上下载ubuntu14.04-desktop版本的iso。
第三步,用解压缩工具打开iso文件,注意不需要全部解压,只需将根目录下的wubi.exe解压出来即可。将ubuntu14.04-desktop.iso和wubi.exe放到第一步新分出来的区。
第四步,开始安装,注意安装时要将网络断开,因为在联网状态下安装,会重新下载ubuntu14.04-desktop.iso,浪费时间。断开网络,双击wubi.exe,弹出窗口选择安装的盘符,用户名和密码,点击确认。系统自动重启,等待安装完成
第五步,重启完之后,14.04会出现系统挂载的问题,进入不了图形界面的系统。我们需要开机,进入紫色的选择ubuntu启动的画面。按键盘E,进入编辑。找到“ro rootflags=sync”,改为“rw rootflags=sync”。再按F10启动。没错,改一个字母,系统就可以在wubi中启动了。
第六步,没错和你想的一样,这种方式只有一次有效。每次开机都要手动修改。但是我们可以通过修改配置文件的方式,使其永久有效。 (a)进入系统后,打开终端,执行 $sudo gedit /etc/grub.d/10_lupin 改动文件的第 150 行,把ro改成rw,保存。 linux ${rel_dirname}/${basename} root=${LINUX_HOST_DEVICE} loop=${loop_file_relative} ro ${args} //修改前 linux ${rel_dirname}/${basename} root=${LINUX_HOST_DEVICE} loop=${loop_file_relative} rw ${args} //修改后 (b)然后更新启动器的配置文件 $sudo update-grub
最后,安装结束
第一步,下载tar.gz的jdk,
执行sudo tar zxvf ./jdk的名字.tar.gz -C 你要指定的路径
第二步,修改jdk的权限
sudo chmod 766 -R 你JDK的路
第三步,按“ctrl+alt+t”打开终端
第四步,输入sudo gedit /etc/profile,系统弹出profile文本文件,在最后加入下面的代码
export JAVA_HOME=你JDK的路径 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:4{JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH
第五步,source /etc/profile 用于重新执行刚修改的初始或文件,使之立即生效
第六步,Ubuntu系统默认安装并使用OpenJDK(usr/lib/jvm/),因此需要手动修改系统默认的JDK sudo update-alternatives --install /usr/bin/java java 你JDK的路径/bin/java 300 sudo update-alternatives --install /usr/bin/javac javac 你JDK的路径/bin/javac 300 sudo update-alternatives --install /usr/bin/jar jar 你JDK的路径/bin/jar 300 sudo update-alternatives --install /usr/bin/javah javah 你JDK的路径/bin/javah 300 sudo update-alternatives --install /usr/bin/javap javap 你JDK的路径/bin/javap 300
第七步,sudo update-alternatives --config java
第八步,至此配置完成,输入java -version、javac或java检查是否配置成功。
ubuntu或是其他linux系统,安装软件时会执行 sudo apt-get install 软件名称 。
观察命令行输出的内容,发现会从网上下载一些安装包,那有些人的问题就来了,下载的文件到哪去了,会不会随着安装东西越多,这些下载的安装包消耗硬盘空间。
首先,下载的安装包存到系统的/var/cache/apt/archives路径下
然后,如何清理他们,执行sudo apt-get clean
本文章并不是给初学者详细详解oracle下如何建立表空间,用户,赋权等命令参数及含义,本文章可能只是对有一定的oracle基础的朋友有所帮助。
最近项目中又开始需要oracle数据库,在搭建环境时,发现自己的oracle命令已经忘了差不多,所以我从网上找来一些语句:可以快速的建表空间,用户并且赋权,由于我现在的项目并不是很严谨,所以像临时表空间,oracle的权限分配等细节,在这里会被忽略。
由于10g开始,oracle的控制台改成网页形式,不但界面改变而且相对的速度也慢了许多,所以我喜欢用命令行的方式操作后台。
1.登陆oracle的sqlplus(windows和Linus都适用)
sqlplus /nolog
connect sysdba as sysdba
show user; //如果成功,输入该命令应该返回“SYS”
2.建表空间语句
CREATE TABLESPACE 表空间的名字 DATAFILE 'E:\oracle\product\10.1.0\oradata\表空间的名字.ORA' SIZE 表空间大小 AUTOEXTEND ON;
3.建立用户
create user 用户名 identified by 用户密码 default tablespace 表空间名字 (temporary TABLESPACE 临时表空间名字);
4.赋权
grant resource,connect,dba to 用户名; //将dba的权限赋给用户
5.配置oracle net manage后,就可以用PL/SQL Developer连接工具访问刚刚建好的表空间了。
需要注意的是,不同版本的oracle的导入导出应符合下面的规则:
不管是从低版本导到高版本,还是高版本导入低版本。导出时需要在原本的版本上导出dump文件,再在需要导入的版本上进行导入(有一些绕)。
1.进入windows的doc窗口(cmd)
2.导出dump文件
exp username/password@服务名 file=文件路径及文件名.dmp //这里的服务名指的是net manage里的连接名
导出某几张表
exp username/password@服务名 tables=table1,table2 file=文件路径及文件名.dmp
3.导入dump文件
imp username/password@服务名 file=文件路径及文件名.dmp full=y
导入某几张表
imp username/password@服务名 tables=table1,table2 file=文件路径及文件名.dmp full=y
tar -zxvf xxx.tar.gz -C 目的目录路径
注意:
1.赋值要用 :=
2.或的关系用 or 表示
declare
prefix VARCHAR2(2); --主表前缀
res_value VARCHAR2(20);
sysdate_char VARCHAR2(20);
begin
prefix := 'QZ';
select to_char(sysdate,'yyyyMMdd') into sysdate_char from dual;
for i in (select col1,col2 from table_name ) loop
if i.col1= 'col1' or i.col2= 'col2' then
res_value := '123456';
end if;
end loop;
res_value := SUBSTR(res_value,0,2); --截取字符串 从第一位(0和1都代表第一位)开始截取2为数
dbms_output.put_line( res_value ); -- 输出语句
end;
最后输出 “12”
1.在firefox地址栏输入about:config
2.选择browser.tabs.loadDivertedInBackground,将值改为true
1.update数据
UPDATE VOL_BASEINFO b
SET (b.vlbi_zzmmm, b.vlbi_mzm) = (select t.zz,t.mz from MY_ZHB_2014 t where t.xh= b.vlbi_xh)
where b.vlbi_xh like '14%';
2.根据身份证号判断男女 decode(mod(to_number(substr('身份证号', 17, 1)), 2), 0, '2', '1') a.先截取身份证的倒数第二位数字 b.被2整除 c.如果余数是0,则为2-女 d.如果余数是不为0,则为1-男
select decode(mod(to_number(substr('身份证号', 17, 1)), 2), 0, '2', '1')
from dual
3.在sql中 insert into 中能插入select 语句,这里要求A和B的表结构是一样的
insert into A select * From B;
如果不一样,则需要使用:
insert into A(C1,C2,...) select C1,C2,... From B;
//这里C1、C2分别指A表与B表字段大小和类型都相同的列。
安装vsftp
apt-get install update
apt-get install vsftpd
查看ftp是否启动
netstat -a|grep ftp*
ftp -localhost
Oracle 10g版本是10201_database_win32,安装提示“程序异常终止,发生未知错误”。
1.修改Oracle 10G\database\stage\prereq\db\refhost.xml 在
本模板主要提供快速创建一个存储过程
本例子中包含:循环游标,事务
USE [数据库名称]
GO
/****** 脚本日期: 11/25/2014 01:05:48 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [用户].[存储过程名称]
@epId varchar(20),
@bizname varchar(150),
@resValue varchar(2) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
declare @epName varchar(500);
declare @belongSepa varchar(6);
declare @processinstid numeric(18, 0);
declare @orgid numeric(10, 0);
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
set @resValue = 0;
declare order_cursor CURSOR LOCAL FORWARD_ONLY KEYSET SCROLL_LOCKS FOR select ep_name,belong_sepa,processinstid,orgid from ENTERPRISE where ep_id = @epId;
OPEN order_cursor
--开始循环游标变量
FETCH NEXT FROM order_cursor INTO @epName,@belongSepa,@processinstid,@orgid;
WHILE (@@FETCH_STATUS = 0)
BEGIN
insert into BAK_OLD_EPINFO values (@epId,@epName,@processinstid,@orgid,@belongSepa,getdate());
if (@@ERROR=0)
BEGIN
SET @resValue=0; --成功
delete from ENTERPRISE where ep_id = @epId;
END
ELSE
BEGIN
SET @resValue=1; --失败
END
FETCH NEXT FROM order_cursor INTO @epName,@belongSepa,@processinstid,@orgid;
END
CLOSE order_cursor
DEALLOCATE order_cursor
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
END
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER PROCEDURE [dbo].[deleteAMANDTPByAMID]
-- Add the parameters for the stored procedure here
@amId varchar(20),
@resValue varchar(2) OUTPUT
--0 成功 1 失败 2 计划在审批
AS
BEGIN
declare @tpid varchar(20);
declare @epidcs varchar(20);
declare @epidcz varchar(20);
declare @processid numeric(18, 0);
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN
select @tpid=tp_id,@epidcs=en_id_cs,@epidcz=en_id_cz,@processid=PROCESSINSTID from TRANSFER_PLAN where am_id=@amId;
if (@processid is not null)
BEGIN
set @resValue = '2';
END
ELSE
BEGIN
insert BAK_OLD_AMTP (am_id,tp_id,ep_id_cs,ep_id_cz,sysdate) values (@amid,@tpid,@epidcs,@epidcz,getdate());
if (@@ERROR=0)
BEGIN
set @resValue = '0';
update AGREEMENT set status='1' where am_id=@amId;
update TRANSFER_PLAN set status='1' where am_id=@amId;
END
ELSE
BEGIN
SET @resValue= '1'; --失败
END
END
END
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
END
如何调用的语句例子
declare @epid varchar(50);
declare @epname varchar(500);
declare @resvalue varchar(500);
set @epid = '123456';
set @epname = 'xxxxxx';
exec [用户].[存储过程名称] @epid,@epname,@resvalue output
print @resvalue;
1.SqlServer中的类型bit(1)对应MySQL的tinyint(1)
2.SqlServer中用getdate()获取数据库系统当前时间;MySQL中NOW()获取数据库系统当前时间
3.SqlServer中转型需要用cast,mysql不需要用cast;
4.SqlServer存储过程中循环中退出需要用break,mysql用leave
第一步
#apt-get update
#apt-get upgrade
#apt-get install postfix libsasl2-2 sasl2-bin libsasl2-modules dovecot-imapd dovecot-pop3d dovecot-common
第二步
首先,停掉这三项服务:
#postfix stop
#service dovecot stop
接下来,修改postfix的配置文件/etc/postfix/main.cf
由于采用了sasl做认证,同时用dovecot作为pop3、imap和smtp的服务,所以需要修改postfix的配置文件以适应需求。
在/etc/postfix/main.cf文件中增加以下信息以支持sasl认证和dovecot服务。
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = yourdomain.com
smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
smtpd_sasl_security_options = noanonymous
message_size_limit = 10240000
另外main.cf中的以下两个参数
myhostname = yourhostname
mydestination = yourdomain.com, localhost.localdomain, localhost
分别代表了你的服务器信息,myhostname是你当前主机名,mydestination其中的yourdomain.com则是邮件服务器名。
修改完毕后保存该文件。
接下来,修改/etc/dovecot/dovecot.conf文件
修改参数protocols为
protocols = pop3 imap
修改mail_location为
mail_location = mbox:~/mail:INBOX=/var/mail/%u (这里需要注意dovecot设置的邮件类型为mbox,而postfix默认邮件格式也能是Maildir。如果这里设置的为mail_location=mail:~/Maildir,会出现收件箱里没有东西)
设置参数disable_plaintext_auth为
disable_plaintext_auth = no
找到auth default,将auth default改名为auth default2。
然后在这行前面增加如下信息
auth default {
mechanisms = plain login
passdb pam {
}
userdb passwd {
}
socket listen {
client {
path = /var/spool/postfix/private/auth
mode = 0660
user = postfix
group = postfix
}
}
}
注意:这里的每个‘{’前都有个空格。
然后保存该文件。
接下来,修改sasl配置文件。
debian默认的安装配置中sasl并不会自动启动,需要修改/etc/default/saslauthd文件。
将其中的START=no修改为START=yes。
修改
OPTIONS="-c -m /var/run/saslauthd"
为
OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd"
然后保存该文件。
在启用sasl的情况下postfix运行需要sasldb2文件在postfix的chroot环境中。
同时为了保证saslauthd能和postfix通讯,需要作如下的修改。
删除位于/va/run目录下的saslauthd目录,然后创建一个指向/var/spool/postfix/var/run/saslauthd的符号连接。
sudo rm -r /var/run/saslauthd/
sudo mkdir -p /var/spool/postfix/var/run/saslauthd
sudo ln -s /var/spool/postfix/var/run/saslauthd /var/run
sudo chgrp sasl /var/spool/postfix/var/run/saslauthd
sudo adduser postfix sasl
另外postfix在运行的时候需要将/etc/sasldb2文件拷贝到chroot环境中。
修改/etc/init.d/postfix文件,修改其中的FILES变量,在其中增加etc/sasldb2
完成以上工作后启动三个服务。
#postfix start
然后新增用户
adduser username
至此,邮件服务器的配置成功。
查询系统邮件日志文件:/var/log/mail.log
首先检查系统中是否安装了sendmail,一般情况都有sendmail,这里要先将sendmail服务关闭,或者是卸载sendmail
#service sendmail stop #关闭sendmail服务
#chkconfig sendmail off #关闭开机自动运行sendmail服务
#postfix start #打开postfix服务
#chkconfig postfix on #开机自动运行postfix服务(我设置后开机任旧不会自动运行postfix)
#service dovecot start #打开dovecot服务
#chkconfig dovecot on #开机自动运行dovecot服务
将postfix加入到root的组:
#usermod -G root postfix
检查服务是否开启,如果服务打开会显示如下结果
#nmap localhost
PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
110/tcp open pop3
如果没有安装nmap命令,可以使用netstat命令
#netstat -nlt
添加邮件服务器用户并分配邮箱 首先添加邮件服务器用户
#adduser username //添加用户
#passwd username //设置密码
由于前面设置mail_location=mbox:~/mail:INBOX=/var/mail/%u,所以,所创建的用户目录下有具有mail目录,如果不存在该目录,如下操作
#telnet ip地址 110
#user username
+OK
#pass password
+login
#chmod 700 /home/username/Maildir //很重要
nginx指定文件路径有两种方式root和alias,这两者的用法区别,使用方法总结了下,方便大家在应用过程中,快速响应。root与alias主要区别在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。
[root] 语法:root path 默认值:root html 配置段:http、server、location、if
[alias] 语法:alias path 配置段:location
实例:
location ~ ^/weblogs/ {
root /data/weblogs/www.ttlsa.com;
autoindex on;
auth_basic "Restricted";
auth_basic_user_file passwd/weblogs;
}
如果一个请求的URI是/weblogs/httplogs/www.ttlsa.com-access.log时,web服务器将会返回服务器上的/data/weblogs/www.ttlsa.com/weblogs/httplogs/www.ttlsa.com-access.log的文件。 [info]root会根据完整的URI请求来映射,也就是/path/uri。[/info] 因此,前面的请求映射为path/weblogs/httplogs/www.ttlsa.com-access.log。
location ^~ /binapp/ {
limit_conn limit 4;
limit_rate 200k;
internal;
alias /data/statics/bin/apps/;
}
alias会把location后面配置的路径丢弃掉,把当前匹配到的目录指向到指定的目录。如果一个请求的URI是/binapp/a.ttlsa.com/favicon时,web服务器将会返回服务器上的/data/statics/bin/apps/a.ttlsa.com/favicon.jgp的文件。
如果你要删除的整个文件夹以及文件夹里面的所有内容的话 rd/s/q 盘符:某个文件夹 (这样整个文件夹所有的文件和文件夹都删除了) 比如我想删除D盘的123文件夹以及123文件夹里面所有的内容 rd/s/q d:123 这样就删除了
如果只是单独删除某个文件的话用 del/f/s/q 盘符:文件名 比如我想删除D盘的456文件夹里面的789这个记事本文件 del/f/s/q d:456789.txt 删除文件的话记住要加上它的后缀名
由于vue2.0官方选择axios来完成 ajax 请求,所以我最近开始用axios写ajax的请求操作。我之前用的架构都是前后端分离,所以必然存在跨域问题。我根据github上axios的官方文档,写了post请求方法,可惜浏览器console控制台中输出跨域问题的错误。
服务端我已经做了http头报文header中的跨域处理
Access-Control-Allow-Origin: *
但是js客户端我按照官方文档操作,发现依然有跨域问题,随后我在网上搜索相关的解决方法,发现需要在js客户端中,也就是http请求报文的头部设置编码格式Content-Type为application/x-www-form-urlencoded
axios({
method:'post',
url:'请求地址',
data:{
param:'参数'
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
}).then(function(res){
return res.data;
});
如果不加这句,post请求是可以访问到服务端,但是服务端不能正常的返回给客户端,并且在浏览器中console控制台中输出跨域错误
如标题所说,本文提供的方法适用于多页面网站,如SPA单页面应用等不适用。
利用window.onpopstate和window.history.pushState 控制浏览器后退键失效,下面说一下如何实现。
1.在你需要禁止浏览器后退键的页面上加上下面的代码:
<script>
$(document).ready(function(){
//判断当前浏览器是否支持history和pushState,据我测试当前大部分浏览器都支持
if(window.history && window.history.pushState){
window.history.pushState({},null,location.href);
}
});
</script>
当你加上上面的代码时,你发现当你点击一次浏览器的后退键,页面并没有后退,你可能感觉好像是解决问题了,但是当你再次点击后退键时页面还是返回到上一页面。
原因是window.history.pushState()方法是向浏览器历史添加了一个状态,它有三个参数分别是,一个状态对象(其实就是页面的参数),一个标题(现在被忽略了),以及一个可选的URL地址。
当你在页面上写了window.history.pushState({},null,location.href);浏览器会在history中添加location.href,同时页面并不跳转,但你只加了一次,所以只对一次后退事件起作用。如何彻底解决这个问题呢?还需要在全局增加onpopstate事件,详情请看第2步。
2.在全局增加一个onpopstate事件,这里我用的是匿名函数(当然也可以直接写):
;(function(window,undefined){
'use strict'
//判断当前浏览器是否支持history和pushState,据我测试当前大部分浏览器都支持
if(window.history && window.history.pushState)
{
window.onpopstate = function(){
window.history.pushState({},null,"");
//window.history.forward(1); 这句我没理解什么意思,不加也可以实现,所以注释掉了
}
}
})(window);
这时你发现,在你的目标页面上点击浏览器回退键页面不会跳转了,问题解决。但是上面的代码是什么意思呢?
window.onpopstate事件会监听浏览器的后退、前进按钮(或者在JavaScript代码中调用类似history.back()、history.forward()、history.go()方法),但是调用像 history.pushState() 或者history.replaceState() 不会触发popstate事件。
所以我们在全局中增加popstate监听事件,一但用户点击后退按钮时就会触发这个方法,该方法会再次向histroy中添加一个链接,防止用户再一次点击后退按钮。
该文章适用于angularjs创建的单页面应用SPA,如vuejs或react.js不在本文讨论范围内。
我用angularjs和sb-admin2实现了前端框架,在不考虑浏览器后退键的情况下,是用$state和$stateParams实现后退功能的,当然也可以自己创建全局单例或是localstroge、sessionstorge处理历史跳转数据。
项目上线后,客户反应当页面跳转到3级或4级页面,点击浏览器后退键时,页面返回上级页面后提示缺少参数,页面的数据也是空白的,为了解决这一问题,我的解决方案是禁用浏览器后退键,下面是解决方法:
在angular.module("appId").run()方法中,用$locationChangeStart监听url地址变化。代码如下:
$rootScope.$on('$locationChangeStart',function(ev,to,from){
if($rootScope.previousState && $rootScope.nowState && $rootScope.previousState == to)
{
console.log('not back');
ev.preventDefault();
}
else{
$rootScope.previousState = from;
$rootScope.nowState = to;
}
});
这样之前的问题就可以避免了,但是我也发现了一个小bug。虽然禁用了浏览器的后退键,用户点击业务页面上的返回按钮时,页面虽然成功返回到上一页面,但是浏览器地址栏的url没有变,当再次点击返回按钮时url又变回正常。
因为对功能没啥影响,就没有再深入了解该问题,如果有高人解决或是了解该问题,望请告知。
我用的是mac pro本,因为硬盘存储空间有限,同时gitbook的客户端用户交互感一般,所以我不推荐下载。
我一般是在线编写文章后,用命令下载同步到本地,在生成html。我喜欢用离线的html文档形式,无论是编写api还是技术文档查看传阅都很方便。
我写这篇文章是2018年2月,引用gitbook升级版频繁并且前后的命令和操作方式不一致,所以请慎重参考。
sudo npm install -g gitbook-cli
mkdir test
cd test
git init #初始化git
git pull http://url.git #将网站上的book下载同步到本地
gitbook build --gitbook=2.6.7
好像从 3.0.0 版起, gitbook build 生成的 website 就不支持 local 打开了(其实也能打开,会有问题如点击左侧目录并不跳转的问题), 必需要 webserver 开启;
实在要完全静态的, 需要以 2.6.7 版build编译( 在有些浏览器下估计不太完美 )
先来看效果图
这里并没引用jquery等第三方库,只是用dom操作和canvas的特性编写的。 canvas画圆大体分为实心圆和空心圆。 根据需求分析知道该圆为实心圆。
//伪代码
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,开始角,结束角);
ctx.fillStyle = 'green';
ctx.closePath();
ctx.fill();
//伪代码
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,绿色开始角,绿色结束角);
ctx.fillStyle = 'green';
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,红色开始角,红色结束角);
ctx.fillStyle = 'red';
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,黄色开始角,黄色结束角);
ctx.fillStyle = 'yellow';
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,紫色开始角,紫色结束角);
ctx.fillStyle = 'purple';
ctx.closePath();
ctx.fill();
动态绘制圆网上普遍推荐三种方法:requestAnimationFrame、setInterval(定时)、还有动态算角度的。 这里我用的是第一种requestAnimationFrame方式。 在编写的过程中发现一个问题,就是动态画圆时并不是以圆心的坐标画的。为了解决这一问题,需要在每次画圆时重新将canvas的画笔的坐标定义为最初圆心的坐标。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
#graph {
/* border: 1px solid black;
height: 100%;
width: 100%;
box-sizing: border-box;*/
}
</style>
</head>
<body>
<div id="circle" style="width: 500px;float: left;"></div>
</body>
</html>
<script type="text/javascript">
(function(window,undefined){
var data = [
{"product":"产品1","sales":[192.44 ,210.54 ,220.84 ,230.11 ,220.85 ,210.59 ,205.49 ,200.55 ,195.71 ,187.46 ,180.66 ,170.90]},
{"product":"产品2","sales":[122.41 ,133.16 ,145.65 ,158.00 ,164.84 ,178.62 ,185.70 ,190.13 ,195.53 ,198.88 ,204.32 ,210.91]},
{"product":"产品3","sales":[170.30 ,175.00 ,170.79 ,165.10 ,165.62 ,160.92 ,155.92 ,145.77 ,145.17 ,140.27 ,135.99 ,130.33]},
{"product":"产品4","sales":[165.64 ,170.15 ,175.10 ,185.32 ,190.90 ,190.01 ,187.05 ,183.74 ,177.24 ,181.90 ,179.54 ,175.98]}
]
var dom_circle = document.getElementById('circle');
if(dom_circle != undefined && dom_circle != null)
{
var canvas = document.createElement("canvas");
dom_circle.appendChild(canvas);
var ctx = canvas.getContext('2d');
var defaultStyle = function(Dom,canvas){
if(Dom.clientWidth <= 300)
{
canvas.width = 300;
Dom.style.overflowX = "auto";
}
else{
canvas.width = Dom.clientWidth;
}
if(Dom.clientHeight <= 300)
{
canvas.height = 300;
Dom.style.overflowY = "auto";
}
else
{
canvas.height = Dom.clientHeight;
}
//坐标轴区域
//注意,实际画折线图区域还要比这个略小一点
return {
p1:'green',
p2:'red',
p3:'yellow',
p4:'purple',
x: 0 , //坐标轴在canvas上的left坐标
y: 0 , //坐标轴在canvas上的top坐标
maxX: canvas.width , //坐标轴在canvas上的right坐标
maxY: canvas.height , //坐标轴在canvas上的bottom坐标
r:(canvas.width)/2, //起点
ry:(canvas.height)/2, //起点
cr: (canvas.width)/4, //半径
startAngle:-(1/2*Math.PI), //开始角度
endAngle:(-(1/2*Math.PI)+2*Math.PI), //结束角度
xAngle:1*(Math.PI/180) //偏移量
};
}
//画圆
var tmpAngle = -(1/2*Math.PI);
var ds = null;
var sum = data[0]['sales'][0]+data[0]['sales'][1]+data[0]['sales'][2]+data[0]['sales'][3]
var percent1 = data[0]['sales'][0]/sum * Math.PI * 2 ;
var percent2 = data[0]['sales'][1]/sum * Math.PI * 2 + percent1;
var percent3 = data[0]['sales'][2]/sum * Math.PI * 2 + percent2;
var percent4 = data[0]['sales'][3]/sum * Math.PI * 2 + percent3;
console.log(percent1);
console.log(percent2);
console.log(percent3);
console.log(percent4);
var tmpSum = 0;
var drawCircle = function(){
if(tmpAngle >= ds.endAngle)
{
return false;
}
else if(tmpAngle+ ds.xAngle > ds.endAngle)
{
tmpAngle = ds.endAngle;
}
else{
tmpAngle += ds.xAngle;
tmpSum += ds.xAngle
}
// console.log(ds.startAngle+'***'+tmpAngle);
// console.log(tmpSum);
// ctx.clearRect(ds.x,ds.y,canvas.width,canvas.height);
if(tmpSum > percent1 && tmpSum <percent2)
{
ctx.beginPath();
ctx.moveTo(ds.r,ds.ry);
ctx.arc(ds.r,ds.ry,ds.cr,ds.startAngle+percent1,tmpAngle);
ctx.fillStyle = ds.p2;
}
else if(tmpSum > percent2 && tmpSum <percent3)
{
ctx.beginPath();
ctx.moveTo(ds.r,ds.ry);
ctx.arc(ds.r,ds.ry,ds.cr,ds.startAngle+percent2,tmpAngle);
ctx.fillStyle = ds.p3;
}
else if(tmpSum > percent3 )
{
ctx.beginPath();
ctx.moveTo(ds.r,ds.ry);
ctx.arc(ds.r,ds.ry,ds.cr,ds.startAngle+percent3,tmpAngle);
ctx.fillStyle = ds.p4;
}
else{
ctx.beginPath();
ctx.moveTo(ds.r,ds.ry);
ctx.arc(ds.r,ds.ry,ds.cr,ds.startAngle,tmpAngle);
ctx.fillStyle = ds.p1;
}
ctx.closePath();
ctx.fill();
requestAnimationFrame(drawCircle);
}
this.toDraw = function(){
ds= defaultStyle(dom_circle,canvas);
// console.log(tmpAngle);
// console.log(ds.xAngle)
ctx.clearRect(ds.x,ds.y,canvas.width,canvas.height);
drawCircle();
}
this.toDraw();
var self = this;
window.onresize = function(){
self.toDraw()
}
}
})(window);
</script>
问题描述:有一串数字1到5,按照下面的关于顺序的要求,重新排列并打印出来。要求如下:2在5前出现,3在2前出现,4在1前出现,1在3前出现。
该问题是一个非常典型的拓扑排序的问题,一般解决拓扑排序的方案是采用DFS-深度优先算法,对于DFS算法我的浅薄理解就是递归,因拓扑排序问题本身会有一些前置条件(本文不过多介绍拓扑算法的定义),所以解决该问题就有了以下思路。
先将排序要求声明成map(把map的key,value看作对顺序的要求,key应在value前出现),然后遍历1-5这几个数,将每次遍历取出的数在map中key查找是否存在,如果存在就按map中key,value的关系,放入结果数组中。再用刚map[key]获取的value去map中的key查找是否存在,如果存在就将新的key和value放入结果数组的一头一尾,以此类推,最终打印结果数组,应满足本题的要求。下面就用Golang实现上述的问题。
package main
import (
"fmt"
"strconv"
)
//edge 要求的顺序
var edge map[string]string = map[string]string{
"2": "5",
"3": "2",
"4": "1",
"1": "3",
}
func main() {
//结果数组
var q []string = make([]string, 0)
//已访问数组
var visited []string = make([]string, 0)
for i := 0; i < 5; i++ {
tupusort(&q, &visited, strconv.Itoa(i))
}
// fmt.Printf("visited: %v \n", visited)
reverse(q)
fmt.Printf("topusort: %v \n", q)
}
//拓扑排序-DFS
func tupusort(q *[]string, visited *[]string, element string) {
if !isVisited(visited, element) {
*visited = append(*visited, element)
if edge[element] != "" {
tupusort(q, visited, edge[element])
}
*q = append(*q, element)
}
}
//检查是否存在已访问的数组中
func isVisited(visited *[]string, element string) bool {
var isVisited bool = false
for _, item := range *visited {
if item == element {
isVisited = true
break
}
}
return isVisited
}
//反转数组顺序
func reverse(arr []string) {
for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 {
arr[i], arr[j] = arr[j], arr[i]
}
}
最后输出结果为
topusort: [4 1 3 2 5 0]
blog list