【canvas系列】canvas实现“ 简单的Amaziograph效果”--画对称图【强迫症福利】

标题很难引人入胜,先放个效果图好了

如果图片吸引不了你,那我觉得也就没啥看的了。

demo链接: https://win7killer.github.io/demo_set/html_demo/canvas/can_demo/draw_roll_2.html

*************************************************

上次“雷达图效果”文章很荣幸,被“某天头条”抓数据抓去了,不开心的是demo链接等所有链接都干掉了~~~  blabla,连个名字都木有。

看的再看下: http://www.cnblogs.com/ufex/p/6655336.html

*************************************************

 创意来源

之前看到的gif效果,为了这个文章又去找了一下。貌似是ipad的app “Amaziograph”。看起来真的很爽,很美

 

配上我自己画的图先:

 

手残不会画画,各位见笑。(手机上浏览器画的哦)

DEMO讲解

1.效果分析

a.参考线坐标轴 -- 为了简单控制参考线显示隐藏,单独一个canvas来搞,也不用每次重绘

b.绘画主体 -- 绘画效果(canvas画线);对称效果(canvas旋转)

c.配置区 -- 简单dom

 

简单来看,很容易实现嘛

 

2.开搞

1> 坐标系统

  其实就是画几条线,但是要均分角度。一种方法是,计算出各个点,然后从中心点发散去画线;另一种是,一边旋转canvas,一边画圆心到统一坐标的线。由于绘画是需用到canvas旋转,所以这里统一使用旋转来处理。

  

那么,就需要先来处理canvas旋转

1 function drawRotate(deg, fn, _ctx) {
2     _ctx = _ctx || ctx
3     _ctx.save();
4     _ctx.translate(_ctx.canvas.width / 2, _ctx.canvas.height / 2);
5     _ctx.rotate(deg);
6     fn && fn(_ctx);
7     _ctx.restore();
8 }

当然,这个是我尝试多次之后写好的方法。

    1、存储ctx状态到栈,

    2、移动旋转点(canvas坐标原点)到canvas中心,

    3、旋转指定角度,

    4、执行绘制函数fn,

    5、从栈里边取回ctx的状态(包含但不仅包含 fillStyle、strokenStyle、translate等等),这里主要处理的是translate,因为我们下次用到坐标会受影响,所以要让canva坐标原点回到原来的位置。

其实这里translate还是比较抽象比较绕的。。。可能我比较迟缓

 

然后,是绘制参考线坐标

 1 function baseLine() {
 2     ctx_role.clearRect(0, 0, ctx_role.canvas.width, ctx_role.canvas.height);
 3     var deg = 360 / pieace;
 4     console.log(deg);
 5     ctx_role.lineWidth = 1;
 6     ctx_role.strokeStyle = 'rgba(0,0,0,.5)';
 7     for (var i = 0, l = pieace; i < l; i  ) {
 8         drawRotate(i * deg / 180 * Math.PI, function(ctx_role) {
 9             draw({
10                 bx: can_role.width / 2,
11                 by: can_role.width / 2,
12                 ex: can_role.width / 2   can_role.width,
13                 ey: can_role.width / 2
14             }, ctx_role);
15         }, ctx_role);
16     }
17 }
1 function draw(option, _ctx) {
2     _ctx = _ctx || ctx;
3     _ctx.beginPath();
4     _ctx.moveTo(option.bx - _ctx.canvas.width / 2, option.by - _ctx.canvas.height / 2);
5     _ctx.lineTo(option.ex - _ctx.canvas.width / 2, option.ey - _ctx.canvas.height / 2);
6     _ctx.stroke();
7 }

 

这样,就绘制完成参考线。

 

2>绘画主体

首先处理一般的画线。跟拖拽效果类似,在move过冲中一直画线链接两个点。对拖拽不了解的可以去了解下,直接上代码

 1 function bindPc() {
 2     can.onmousedown = function(e) {
 3         if (e.button != 0) {
 4             return false;
 5         }
 6 
 7         var op = {};
 8         op.ex = op.bx = e.clientX - can.parentElement.offsetLeft   window.scrollX;
 9         op.ey = op.by = e.clientY - can.parentElement.offsetTop   window.scrollY;
10         drawFn(op);
11         document.onmousemove = function(e) {
12             document.body.style.cursor = 'pointer';
13             op.bx = op.ex;
14             op.by = op.ey;
15             op.ex = e.clientX - can.parentElement.offsetLeft   window.scrollX;
16             op.ey = e.clientY - can.parentElement.offsetTop   window.scrollY;
17             drawFn(op);
18         };
19         document.onmouseup = function() {
20             document.body.style.cursor = 'default';
21             document.onmouseup = document.onmousemove = null;
22         };
23     };
24 }

 

1 function drawFn(op) {
2     var deg = Math.floor(360 / pieace);
3     for (var i = 0, l = 360; i < l; i  = deg) {
4         drawRotate(i / 180 * Math.PI, function(ctx) {
5             draw(op);
6         });
7     }
8 }

需要注意,e.button 用来判断是鼠标哪个键,0是左键

这里又用到了前边的drawRotate 和 draw。

 

************************************

至此,应该可以画出对称的线条了。

以下就是锦上添花的事情了

************************************

增加移动端的绘制支持(惭愧,没怎么写过移动端,欢迎多指教)

 1 function bindWp() {
 2     can.addEventListener('touchstart', function(e) {
 3         op = can.op = {};
 4         op.ex = op.bx = e.touches[0].clientX - can.parentElement.offsetLeft   window.scrollX;
 5         op.ey = op.by = e.touches[0].clientY - can.parentElement.offsetTop   window.scrollY;
 6         drawFn(op);
 7         can.addEventListener('touchmove', touchMoveFn);
 8         can.addEventListener('touchend', touchEndFn);
 9     });
10 
11     function touchEndFn() {
12         document.body.style.cursor = 'default';
13         can.removeEventListener('touchmove', touchMoveFn);
14         can.removeEventListener('touchend', touchEndFn);
15     }
16 
17     function touchMoveFn(e) {
18         op = can.op;
19         document.body.style.cursor = 'pointer';
20         op.bx = op.ex;
21         op.by = op.ey;
22         op.ex = e.touches[0].clientX - can.parentElement.offsetLeft   window.scrollX;
23         op.ey = e.touches[0].clientY - can.parentElement.offsetTop   window.scrollY;
24         drawFn(op);
25         return false;
26     }
27 }

 

3>设置等

这里dom比较简单,就略过了。只说一项,下载canvas图片到本地

最简单的,右键保存图片到本地,但是你肯定会骂我傻,谁不知道这操作啊;那么就来稍微装X一下吧

线上代码

1 function download() {
2     var data = can.toDataURL('image/png', 0.8);
3     var $a = document.createElement('a');
4     $a.download = imgName.value || 'default.png';
5     $a.target = '_blank';
6     $a.href = data;
7     $a.click();
8 }

(写这个博客的时候,返现自己把这个方法写麻烦了,绕远了。/手动尴尬,这里直接改了)

关键点在于  a.download属性,这个是把文件下载到本地的关键哦,然后要把canvas转成base64(canvas.toDataUrl方法,不清楚的可以去去了解下,这里不再赘述)

 

******************************************************

最后,附上完整代码(可能会和上边的有点出如,还在调整)

<!DOCTYPE html>
<html lang="zh"><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"><mtea author="win7killer@163.com"></mtea><title>Document</title><style>* {margin: 0;padding: 0;}p {line-height: 15px;font-size: 12px;}@media screen and (max-width: 768px) {.wrapper {width: auto;position: relative;overflow: hidden;}}@media screen and (min-width: 769px) {.wrapper {width: 600px;height: 600px;margin: 100px auto 0;position: relative;overflow: hidden;}#panel_box {position: fixed;top: 20px;right: 20px;width: 200px;}}canvas {background: #fafafa;display: block;}#can_role {background: none;position: absolute;top: 0px;left: 0px;pointer-events: none;}#panel_box {padding: 10px;margin-top: 10px;border: 1px solid rgba(10, 10, 10, .7);box-shadow: 10px 7px 10px #999;z-index: 100;}input {width: 80px;margin-left: 20px;}label {text-align: justify;}</style>
</head><body><div class="wrapper" id="wrapper"><canvas id="can_role"></canvas><canvas id="can"></canvas></div><div id="panel_box"><p><label>画笔颜色<input id="color_val" type="color" value="#0099ff"/></label></p><p><label>画笔宽度<input type="number" id="line_width_val" min="1" max="20" value="2"/></label></p><p><label>扇形份数<input type="number" id="pieaceNum" min="1" max="200" value="12"/></label></p><p><label>参考线<input type="checkbox" id="onOff" checked="checked"/></label></p><p class="img_name_box"><label>图片名称<input type="text" id="imgName" placeholder="ex:test.png"></label></p><p><a href="javascript:;" id="save_btn" target="">下载到本地</a></p></div><script>var pieace = 6;var ctx = can.getContext('2d');var ctx_role = can_role.getContext('2d');can.width = can.height = can_role.width = can_role.height = window.screen.width > 768 ? 600 : window.screen.width;ctx_role.lineJoin = ctx.lineJoin = "round";ctx_role.lineCap = ctx.lineCap = "round";function drawFn(op) {var deg = Math.floor(360 / pieace);for (var i = 0, l = 360; i < l; i  = deg) {drawRotate(i / 180 * Math.PI, function(ctx) {draw(op);});}}function draw(option, _ctx) {_ctx = _ctx || ctx;_ctx.beginPath();_ctx.moveTo(option.bx - _ctx.canvas.width / 2, option.by - _ctx.canvas.height / 2);_ctx.lineTo(option.ex - _ctx.canvas.width / 2, option.ey - _ctx.canvas.height / 2);_ctx.stroke();}function drawRotate(deg, fn, _ctx) {_ctx = _ctx || ctx_ctx.save();_ctx.translate(_ctx.canvas.width / 2, _ctx.canvas.height / 2);_ctx.rotate(deg);fn && fn(_ctx);_ctx.restore();}function baseLine() {ctx_role.clearRect(0, 0, ctx_role.canvas.width, ctx_role.canvas.height);var deg = 360 / pieace;ctx_role.lineWidth = 1;ctx_role.strokeStyle = 'rgba(0,0,0,.5)';for (var i = 0, l = pieace; i < l; i  ) {drawRotate(i * deg / 180 * Math.PI, function(ctx_role) {draw({bx: can_role.width / 2,by: can_role.width / 2,ex: can_role.width / 2   can_role.width,ey: can_role.width / 2}, ctx_role);}, ctx_role);}}function download() {var data = can.toDataURL('image/png', 0.8);var $a = document.createElement('a');$a.download = imgName.value || 'default.png';$a.target = '_blank';$a.href = data;$a.click();// if (typeof MouseEvent === 'function') {//     var evt = new MouseEvent('click', {//         view: window,//         bubbles: true,//         cancelable: false//     });//     $a.dispatchEvent(evt);// }}function bindPc() {can.onmousedown = function(e) {if (e.button != 0) {return false;}var op = {};op.ex = op.bx = e.clientX - can.parentElement.offsetLeft   window.scrollX;op.ey = op.by = e.clientY - can.parentElement.offsetTop   window.scrollY;drawFn(op);document.onmousemove = function(e) {document.body.style.cursor = 'pointer';op.bx = op.ex;op.by = op.ey;op.ex = e.clientX - can.parentElement.offsetLeft   window.scrollX;op.ey = e.clientY - can.parentElement.offsetTop   window.scrollY;drawFn(op);};document.onmouseup = function() {document.body.style.cursor = 'default';document.onmouseup = document.onmousemove = null;};};}function bindWp() {can.addEventListener('touchstart', function(e) {op = can.op = {};op.ex = op.bx = e.touches[0].clientX - can.parentElement.offsetLeft   window.scrollX;op.ey = op.by = e.touches[0].clientY - can.parentElement.offsetTop   window.scrollY;drawFn(op);can.addEventListener('touchmove', touchMoveFn);can.addEventListener('touchend', touchEndFn);});function touchEndFn() {document.body.style.cursor = 'default';can.removeEventListener('touchmove', touchMoveFn);can.removeEventListener('touchend', touchEndFn);}function touchMoveFn(e) {op = can.op;document.body.style.cursor = 'pointer';op.bx = op.ex;op.by = op.ey;op.ex = e.touches[0].clientX - can.parentElement.offsetLeft   window.scrollX;op.ey = e.touches[0].clientY - can.parentElement.offsetTop   window.scrollY;drawFn(op);return false;}}function bindSets() {color_val.onchange = function() {ctx.strokeStyle = color_val.value;}line_width_val.onchange = function() {ctx.lineWidth = line_width_val.value;}pieaceNum.onchange = function() {ctx.clearRect(0, 0, can.width, can.height);reset();}onOff.onchange = function() {if (this.checked == true) {can_role.style.display = 'block';} else {can_role.style.display = 'none';}}}function bind() {bindPc();bindWp();bindSets();save_btn.onclick = download;}function reset() {pieace = pieaceNum.value;ctx.strokeStyle = 'rgba(100,100,100,.7)';baseLine();ctx.lineWidth = line_width_val.value;ctx.strokeStyle = color_val.value;}function init() {reset();bind();}init();</script>
</body></html>

  

**************偷偷留个名字,防抓  博客园-fe-bean***************

涉及姿势点总结  

 

1.canvas_translate

2.canvas_rotate

3.canvas_toDataUrl

4.a.download  &&  base64

其余的想起来再添加吧

 

最后,欢迎大家多提意见、交流,点赞转载那就更棒了。

再丢一张图

 

下期再见咯~~~

 

 

 

 

****************   少侠留步,能看到这里的,我要给你们一个奖励   ***************

这个demo是可以在移动端玩的,意味着有电容笔的亲,可以爽啊~(个别浏览器脑残会左右来回跑~~)

没有电容笔的亲,肯定是大多数,我们一样能玩啊!!!

叫你们快速做一款电容笔(当然没那么好用)

1.找一只木质铅笔

2.削出铅笔头

3.把铅笔头斜着磨平,如图

4.用磨平这一侧去电容屏上画(开始吧)

我上边那张图就是拿铅笔画的~~~

************************************

 


更多专业前端知识,请上 【猿2048】www.mk2048.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361628.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python3基础:字符串、文本文件

字符串&#xff1a; 练习1&#xff1a; str "大胖三百磅不是二百磅陪着一百磅的小胖" print(str.replace("磅", "斤")) # 替换所有 print(str.replace("磅", "斤", 2)) # 替换两次len len(str) # 这句话的字数长度 pri…

[Python][小知识][NO.3] Python 使用系统默认浏览器打开指定URL的网址

1、前言 一般用到的地方&#xff1a; GUI交互界面下&#xff0c;单击某个按钮实现打开指定网址。 某帮助菜单项目&#xff0c;需要跳转网页显示时。 O.O 某XX程序&#xff0c;需要植入网页弹窗广告时... 2、方法 调用 webbrowser 包中的 open 函数即可。 (没安装该包的 CMD命令…

MyEclipse 10优化技巧

MyEclipse 10优化速度方案仍然主要有这么几个方面&#xff1a;去除无需加载的模块、取消冗余的配置、去除不必要的检查、关闭更新。第一步: 去除不需要加载的模块一个系统20%的功能往往能够满足80%的需求&#xff0c;MyEclipse也不例外&#xff0c;我们在大多数时候只需要20%的…

HTML知识点总结之img、scirpt、link标签

<img>元素 使用<img>可以在网页插入一个图片&#xff0c;但实际上<img>标签并不会在网页中直接插入图像&#xff0c;而是从网页上链接图像。 <img>的主要属性 &#xff08;1&#xff09;src属性&#xff1a;图片的路径。 &#xff08;2&#xff09;alt…

laravel中的自定义函数的加载和第三方扩展库加载

一.自定义公共函数 1. 创建文件 app/Helpers/functions.php 2. 修改项目 composer.json 3.运行composer dump-auto 4.OK&#xff0c;然后你就可以在任何地方用到 app/Helpers/functions.php 中的函数了。 二.添加第三方扩展库 1.确定你要放第三方库的目录&#xff0c;比如还是刚…

HDU 1312 Red and Black

这题就是比较水的一道搜索题了&#xff0c;BFS跟DFS都能做&#xff0c;直接看代码吧&#xff01; AC code&#xff1a; View Code 1 #include <iostream> 2 #define MAX 50 3 using namespace std; 4 int w, h; 5 char map[MAX][MAX]; 6 int dir[][2] {{0, 1}, {1, 0},…

Unity3D笔记十七 Unity3D生命周期

一个游戏组件的脚本有一个生命周期——一开始实例化&#xff0c;直到结束实例被销毁。在这期间&#xff0c;他们有时候处于激活状态&#xff0c;有时候处于非激活状态&#xff1b;对于活动&#xff0c;对用户有时候可见&#xff0c;有时候不可见 本文主要讨论常见脚本的的生命周…

自适应堆大小

在改进我们的测试平台以改进Plumbr GC问题检测器的同时 &#xff0c;我最终编写了一个小型测试用例&#xff0c;我认为这对于更广泛的读者来说可能很有趣。 我追求的目标是测试JVM在eden&#xff0c;survivor和Tenured空间之间如何分割堆方面的自适应性。 测试本身正在成批生成…

错误笔记

1、user_name a and password b时&#xff0c;无法打印到这个节点&#xff0c;原因是 a "yajuan" b 123456时 a 为字符串类型&#xff0c;b为数字类型&#xff0c;类型不同“且”的关系不成立。导致if 节点失败 转载于:https://www.cnblogs.com/wangyajuanjuan…

第一次Java 8体验

像世界其他地方一样&#xff0c;我深深地爱上了Slack。 为什么&#xff1f; 原因很多&#xff0c;但主要的原因是它提供了一种围绕通讯而非工具真正构建SDLC流程的新方法。 您认为这些天哪个更常见&#xff0c;杂乱无章的机智团队在荒野中四处徘徊&#xff0c;尽管他们有出色的…

七个重要习惯——读《高效能人士的七个习惯》整理

个人的成功习惯一&#xff1a;积极主动习惯二&#xff1a;以始为终习惯三&#xff1a;要事第一 公众的成功习惯四&#xff1a;双赢思维习惯五&#xff1a;知彼解己习惯六&#xff1a;综合综效 习惯七&#xff1a;不断更新 附图&#xff1a; 转载于:https://www.cnblogs.com/ziq…

POJ2941 SDUT2371Homogeneous squares

View Code 1 #include<stdio.h> 2 #include<string.h> 3 int main() 4 { 5 long i,j,n,g,s,t,a[1001],b[1001]; 6 char str[8001];//这里数组开大一点 第一次RT了 数比较大 7 while(scanf("%ld", &n)&&n) 8 { 9 s …

不变性如何提供帮助

在最近的几篇文章中&#xff0c;包括“ Getters / Setters。 邪恶。 期。” &#xff0c; “对象应该是不可变的”和“依赖注入容器是代码污染者” &#xff0c;我普遍将所有可变对象标记为“ setter”&#xff08;以set开头的对象方法&#xff09;。 我的论证主要基于隐喻和抽…

浅谈.Net版(C#)的CMP模式

商城上线快2、3个月了&#xff0c;一直都懒得写点东西&#xff0c;在加上杂七杂八的事情也比较忙&#xff0c;所以都没有把这个系统当时做的整个架构思绪整理清&#xff0c;昨天才从深圳完了两天回来&#xff0c;怎感觉是要做的事来着.刚开始接触CMP模式的时候也是看了它几天,到…

Java Servlet教程– ULTIMATE指南(PDF下载)

Java Servlets是一种基于Java的Web技术。 Java Servlet技术为Web开发人员提供了一种简单&#xff0c;一致的机制&#xff0c;以扩展Web服务器的功能并访问现有的业务系统。 几乎可以将Servlet看作是在服务器端运行的applet&#xff0c;它没有任何表情。 Java servlet使许多Web…

使用WSO2开发

几个月以来&#xff0c;我又开始使用WSO2产品。 在接下来的文章中&#xff0c;我描述了我遇到的一些&#xff08;小&#xff09;问题以及如何解决它们。 设置开发环境时&#xff0c;我要做的第一件事是在Mac上下载Developer Studi o&#xff08;64位版本&#xff09;。 解压缩…

CSS揭秘(二)背景与边框

Chapter2 背景与边框 1. 半透明边框 基础&#xff1a;了解 RGBA & HSLA 颜色&#xff08;色调 0~360、饱和度、亮度 &#xff08;0%黑色~100%白色&#xff09;、透明度&#xff09; 默认情况下&#xff0c;背景在边框的下层&#xff0c;容器的背景从半透明的边框透上来并…

Hibernate 拦截器 Hibernate 监听器

Hibernate拦截器(Interceptor)与事件监听器(Listener) 拦截器&#xff08;Intercept&#xff09;&#xff1a;与Struts2的拦截器机制基本一样&#xff0c;都是一个操作穿过一层层拦截器&#xff0c;每穿过一个拦截器就会触发相应拦截器的事件做预处理或善后处理。  监听器&am…

深入探讨用位掩码代替分支(3):VC6速度测试

wuhanbingwhdx提到了数据相关也会影响流水线&#xff08;http://blog.csdn.net/zyl910/article/details/1330614&#xff09;。  他的说法是有一定道理的。但是&#xff0c;在很多时候我们并不仅仅处理一个数值。比如将循环展开&#xff0c;在内循环处理2个或更多个的数值。而…

Web安全相关(三):开放重定向(Open Redirection)

简介 那些通过请求&#xff08;如查询字符串和表单数据&#xff09;指定重定向URL的Web程序可能会被篡改&#xff0c;而把用户重定向到外部的恶意URL。这种篡改就被称为开发重定向攻击。 场景分析 假设有一个正规网站http://nerddinner.com/&#xff0c;还有一个恶意网站或钓鱼…