GithubHelp home page GithubHelp logo

html-css-note's People

Contributors

cy0707 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

html-css-note's Issues

SVG学习笔记之四

浏览器的的坐标系统

浏览器的的坐标系统的原点是屏幕的左上角,向右是X轴的正方向,向下是Y轴的正方向。

SVG的坐标系统

  • 最初坐标系统
  • 嵌套坐标系统
  • 转换坐标系统

最初坐标系统

SVG的最初坐标签系统和viewport(视窗)的坐标系统是相同的,都是在左上角原点处,沿y轴向下和x轴向右延伸。也就是说,SVG的坐标系统类似于Viewport的坐标系统,原点(0,0)在左上角处,不管使用单位还是不使用单位,其都是沿y轴向下垂直延伸,沿x轴向右延伸。

<svg width="300" height="300"></svg>

那么这个SVG的大小是300*300,其是由坐标(0,0)、(300,0)、(300,300)和(0,300)组成的一个矩形画布:

这里没有设置具体的单位值,表示的就是SVG画布大小是300*300个单位,默认情况下就是px。

嵌套坐标系统

在元素中可以嵌套元素。外面的创建一个Viewport和坐标系统,而且嵌套在里面的也可以创建一个Viewport和坐标系统。这种方式就是在大的画布中绘制一个小的画布.

转换坐标系统

数学矩阵相关知识进行变换的坐标系

SVG文件结构

一个独立的SVG文件,也就是平时看到的以扩展名.svg结尾的文件,他主要包括:

  • XML声明
  • <svg>根元素包括一个用来描述SVG的XML声明空间。

其中XML声明其实和HTML文档的DTD声明是类似的。大家是否还记得HTML相关的DTD声明方式。比如HTML5的声明方式:

<!DOCTYPE html>

SVG文件使用的是XML声明方式:

<?xml version="1.0"?>

第二部分是SVG的XML声明空间,这一部分类似于HTML中的xmlns="http://www.w3.org/1999/xhtml":

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

而SVG文件中的声明空间是xmlns="http://www.w3.org/2000/svg:

<svg xmlns="http://www.w3.org/2000/svg">

其实在实际使用中,他也分为几种不同的方式。

最小的SVG结构

其实最小的SVG结构就是一种简写的结构:

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg">
    <!-- SVG代码 -->
</svg>

典型的SVG结构

除了简写的SVG结构,还有一种典型的SVG结构。通常情况之下,一个SVG文件包含内部链接,在这种情况之下,必须定义xlink声明空间http://www.w3.org/1999/xlink:

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="300">
    <!-- SVG代码 -->
</svg>

在这个示例中,还声明了SVG画布的大小。定义SVG画布大小是可选的,但强烈推荐,使用SVG时定义其画布大小。

在使用代码编写SVG时,最好加上DTD,这是非常有用的。在一些示例中,你可能会看到这样的代码:

<?xml version="1.0" ?>
<!DOCTYPE svg  PUBLIC '-//W3C//DTD SVG 1.1//EN'  'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg enable-background="new 0 0 145 145" id="Layer_1" version="1.1" viewBox="0 0 145 145" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g>
        <path d="M95.727,56.11c-2.29-3.814-4.565-6.092-4.617-6.146c-0.48-0.48-2.289,1.668-2.791,2.309   c-0.762,0.981-2.563,2.625-6.367,4.876c-3.802,2.255-9.599,5.132-18.35,8.687c-3.747,1.524-6.766,3.085-9.192,4.666   c3.136-0.364,6.856-0.784,7.613-0.815c2.007-0.082-0.404,4.203-9.474,2.116c-1.186,0.895-2.195,1.796-3.047,2.699   c-1.388,1.474-2.355,2.959-2.971,4.422c-0.617,1.463-0.877,2.9-0.876,4.246c0.005,3.039,1.285,3.753,2.512,5.495   c1.234,1.746,3.872,2.962,3.872,2.962s-0.704-1.33-1.719-2.789c-1.022-1.463-1.976-3.455-1.971-5.668   c0.001-1.004,0.188-2.065,0.665-3.201c0.275-0.653,0.652-1.335,1.149-2.038c0.466,2.206,1.478,6.081,3.454,10.021   c1.499,2.98,3.555,4.208,6.406,6.524c2.844,2.317,6.521,5.686,11.017,5.679c0.11,0,0.221-0.001,0.332-0.003   c3.876-0.057,7.15-3.391,9.724-5.757c3.87-3.555,6.308-7.082,7.847-12.521c1.531-5.446,2.713-11.542,3.009-15.689   c0.522-7.306,0.163-10.061-0.246-11.266c0.572,0.787,1.188,1.696,1.808,2.743c2.096,3.534,4.127,8.399,4.123,13.856   c-0.002,3.122-0.653,6.447-2.35,9.907c-1.698,3.459-4.452,7.06-8.7,10.68c0,0,9.238-5.66,11.119-9.493   c1.882-3.831,2.628-7.595,2.626-11.095C100.33,65.29,98.012,59.922,95.727,56.11z M77.582,69h11.677C89.259,69,89.259,75,77.582,69   z"/>
        <path d="M53.943,97.604c-0.348-0.031-0.705-0.008-1.062-0.028c-0.212-0.012-0.425-0.001-0.633-0.02   c-3.854-0.352-6.887-1.923-8.909-4.354c-2.018-2.434-3.053-5.744-2.744-9.682l0.018-0.214c0.262-2.885,1.129-5.415,2.495-7.631   c1.367-2.215,3.437-3.863,5.531-5.702c7.384-6.483,14.57-10.075,21.95-13.905c4.245-2.203,8.488-4.594,12.651-7.22   c0.93-0.589,1.652-1.372,2.303-2.16c0.65-0.79,1.234-1.593,1.838-2.262c0,0-8.906,4.272-12.152,5.812   c-9.81,4.656-19.593,9.548-28.099,16.587c-3.033,2.512-5.808,5.679-7.739,9.131c-1.279,2.286-2.037,4.649-2.252,7.027   c-0.347,3.803,0.713,7.618,3.108,11.164c1.28,1.9,2.797,3.31,4.487,4.276c1.689,0.967,3.541,1.487,5.471,1.66   c1.797,0.162,3.675-0.072,5.585-0.411l7.056-1.355l-7.128-0.644C55.143,97.622,54.545,97.659,53.943,97.604z"/>
        <path d="M49.823,71.043c0.97,0.317,1.875,0.565,2.726,0.76c0.576-0.435,1.197-0.869,1.86-1.301   C51.934,70.79,49.823,71.043,49.823,71.043z" fill="#FFFFFF"/>
    </g>
</svg>

HTML5中的SVG

最简单的方法,你只需要将.svg中所有代码复制,然后粘贴到.html文件中,但有两点需要注意:

  • 在.html文件中,粘贴了.svg代码之后,要将SVG的XML声明删除
  • HTML文件应该使用HTML5的DTD
    最终代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SVG绘制的MM头像</title>
</head>
<body>
    <svg enable-background="new 0 0 145 145" id="Layer_1" version="1.1" viewBox="0 0 145 145" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300">
        <g>
            <path d="M95.727,56.11c-2.29-3.814-4.565-6.092-4.617-6.146c-0.48-0.48-2.289,1.668-2.791,2.309   c-0.762,0.981-2.563,2.625-6.367,4.876c-3.802,2.255-9.599,5.132-18.35,8.687c-3.747,1.524-6.766,3.085-9.192,4.666   c3.136-0.364,6.856-0.784,7.613-0.815c2.007-0.082-0.404,4.203-9.474,2.116c-1.186,0.895-2.195,1.796-3.047,2.699   c-1.388,1.474-2.355,2.959-2.971,4.422c-0.617,1.463-0.877,2.9-0.876,4.246c0.005,3.039,1.285,3.753,2.512,5.495   c1.234,1.746,3.872,2.962,3.872,2.962s-0.704-1.33-1.719-2.789c-1.022-1.463-1.976-3.455-1.971-5.668   c0.001-1.004,0.188-2.065,0.665-3.201c0.275-0.653,0.652-1.335,1.149-2.038c0.466,2.206,1.478,6.081,3.454,10.021   c1.499,2.98,3.555,4.208,6.406,6.524c2.844,2.317,6.521,5.686,11.017,5.679c0.11,0,0.221-0.001,0.332-0.003   c3.876-0.057,7.15-3.391,9.724-5.757c3.87-3.555,6.308-7.082,7.847-12.521c1.531-5.446,2.713-11.542,3.009-15.689   c0.522-7.306,0.163-10.061-0.246-11.266c0.572,0.787,1.188,1.696,1.808,2.743c2.096,3.534,4.127,8.399,4.123,13.856   c-0.002,3.122-0.653,6.447-2.35,9.907c-1.698,3.459-4.452,7.06-8.7,10.68c0,0,9.238-5.66,11.119-9.493   c1.882-3.831,2.628-7.595,2.626-11.095C100.33,65.29,98.012,59.922,95.727,56.11z M77.582,69h11.677C89.259,69,89.259,75,77.582,69   z"/>
            <path d="M53.943,97.604c-0.348-0.031-0.705-0.008-1.062-0.028c-0.212-0.012-0.425-0.001-0.633-0.02   c-3.854-0.352-6.887-1.923-8.909-4.354c-2.018-2.434-3.053-5.744-2.744-9.682l0.018-0.214c0.262-2.885,1.129-5.415,2.495-7.631   c1.367-2.215,3.437-3.863,5.531-5.702c7.384-6.483,14.57-10.075,21.95-13.905c4.245-2.203,8.488-4.594,12.651-7.22   c0.93-0.589,1.652-1.372,2.303-2.16c0.65-0.79,1.234-1.593,1.838-2.262c0,0-8.906,4.272-12.152,5.812   c-9.81,4.656-19.593,9.548-28.099,16.587c-3.033,2.512-5.808,5.679-7.739,9.131c-1.279,2.286-2.037,4.649-2.252,7.027   c-0.347,3.803,0.713,7.618,3.108,11.164c1.28,1.9,2.797,3.31,4.487,4.276c1.689,0.967,3.541,1.487,5.471,1.66   c1.797,0.162,3.675-0.072,5.585-0.411l7.056-1.355l-7.128-0.644C55.143,97.622,54.545,97.659,53.943,97.604z"/>
            <path d="M49.823,71.043c0.97,0.317,1.875,0.565,2.726,0.76c0.576-0.435,1.197-0.869,1.86-1.301   C51.934,70.79,49.823,71.043,49.823,71.043z" fill="#FFFFFF"/>
        </g>
    </svg>
</body>
</html>

参考文章

SVG系列教程:SVG文件结构
SVG系列教程:坐标系统

HTML5拖拽笔记之一

定义可拖动目标

在一个web页面中,有一些默认就可以被拖动的元素。它们包括选中的文本,images图片和链接。当一个图片或者链接被拖动时,这个被拖动的图片或者链接的URL就会先被设置为“拖动数据”(drag data),然后一次拖动过程就正式开始了。

对于其他元素,若你要查看它们默认的拖动效果,它们必须是选中区域的一部分。查看默认的拖动效果:在一个web页面上选择一个区域,然后点击并按住鼠标,然后拖动选中区域。

在HTML中,除了图片、超链接以及被选中区域,其它元素默认是不可拖拽的。为了让别的HTML元素也能被拖拽,必须进行以下三步:

  • 为所需拖拽的元素设置属性 draggable属性。
  • 为 dragstart 事件添加侦听
  • 在侦听中设置拖拽数据。
<div draggable="true" ondragstart="event.dataTransfer.setData('text/plain', 'This text may be dragged')">
  This text <strong>may</strong> be dragged.
</div>

属性 draggable 被设置为 true,则元素变为可拖拽。如果该属性被忽略或者设置为 false,则元素不能被拖拽,文本可以被选中。

该属性可被用于所有元素,包括图片和链接。然而,对于图片和链接,是默认可以拖拽的。故只需对默认属性值为false的元素设置该值。

一旦元素为可拖拽,元素中的文本或其他元素就无法通过鼠标点击或拖拽来实现选中了。但,可通过Alt+鼠标选中文本。(只有Firefox中才可以用Alt键选中)

拖拽操作一开始,dragstart事件就会被触发。在这个例子中,dragstart事件监听器被添加到可拖拽元素本身之中,但你也可以在其祖先元素中进行监听,因为正如其它大部分事件一样,拖拽事件是会冒泡的。

在该事件中,你可以指定拖拽数据,反馈图片,以及拖拽效果。一般,只有拖拽数据是必须的,其他都可以自适应的。

拖拽数据

所有的拖拽事件都有一个属性——dataTransfer,它包含着拖拽数据。

拖拽发生时,数据需与拖拽关联,以标识谁在被拖拽。如,当拖拽文本框中的选中文本时,关联到拖拽的数据即是文本本身。类似,拖拽网页上的链接时,拖拽数据即是链接的URL。

拖拽数据包含两类信息,类型(type)或者格式(format)或者数据(data),和数据的值(data value)。

格式即是一个表示类型的字符串(如,对于文本数据来说,格式为 "text/plain"),数据值为文本字串。当拖拽开始时,你需要通过提供一个类型以及数据来为拖拽添加数据。拖拽过程中,在dragenter 和 dragover 事件侦听中,需根据数据类型检测是否支持 drop(放下)。

如,接收超链接的 drop 目标会检测是否支持链接类型 text/uri-list。在drop事件中, 侦听者(即事件处理函数)会获取拖拽中的数据并将其插入到 drop 位置。

类型指的是 MIME-type ,与 string 类似,如 text/plain 或者 image/jpeg。也可以自定义类型。常用类型列表参见:Drag Types。

一次拖拽可能会提供多种不同类型的数据,这样就可以提供更详细的数据了(通常是自定义类型的数据)。可以为drop目标体提供后备数据,如果这些目标体不支持更具体的类型的数据的话。通常情况下,至少要指定类型为文本数据,即 text/plain 类型。看看下面的例子。

使用 setData 方法在 dataTransfer 中设置数据。需要提供两个参数:数据类型和数据值。例如:
在这个例子中,数据值是"Text to drag",它的格式是"text/plain"

event.dataTransfer.setData("text/plain", "Text to drag");

可以提供多种格式的数据。通过多次调用 setData 方法增加不同的格式。格式顺序需从 具体 到 一般。

var dt = event.dataTransfer;
dt.setData("application/x-bookmark", bookmarkString);
dt.setData("text/uri-list", "http://www.mozilla.org");
dt.setData("text/plain", "http://www.mozilla.org");

在这里,数据被以三种类型添加。第一个类型“application/x-bookmark'”是一个自定义类型。一般来说别的应用是不支持这种类型的,但可以在同一个网站或应用内的拖拽中使用这样的自定义类型。

与此同时,通过提供更为一般的数据类型,你也可以使这一拖拽被其它应用支持。 'application/x-bookmark'类型的数据你可以设置得具体一些,为了使其它应用支持而设置的较为一般的数据类型的数据可以简单些,比如仅仅设置一个URL或者一段文本。

注意:本例中"text/uri-list"和"text/plain"类型的数据内容都相同。这样的设定经常发生,但也不是非这么设定不可。

若你试图两次以同一格式设定数据,新数据会替代旧数据,数据的位置还是和旧的一样。

你可以使用 clearData 方法来移除一个数据。需要传入一个参数:待移除的数据的类型。

event.dataTransfer.clearData("text/uri-list");

clearData 方法的参数是可选的。如果没有传入参数,绑定其上的所有数据都会被移除。若没有设定数据,或者数据全被移除了,那么拖拽不会发生。

设置拖动反馈图片(仅Firefox支持)

当一次拖拽发生时,会从被拖拽的目标(即dragstart事件被触发的元素)处产生一个半透明的图片(反馈图片),该图片在拖拽过程中会跟随鼠标指针移动。这个图片是自动生成的,因此你无需亲自设定它。但你可以使用 setDragImage 来自定义一个反馈图片。下面这个例子就生成了一个叉叉的拖动图片,当让这个效果只能在最新Firefox上面看到。

<div draggable="true" ondragstart="dragWithCustomImage(event);">
  This text <strong>may</strong> be dragged.
  <script type="text/javascript">
  	function dragWithCustomImage(event)
  	{
  	  var canvas = document.createElementNS("http://www.w3.org/1999/xhtml","html:canvas");
  	  canvas.width = canvas.height = 50;

  	  var ctx = canvas.getContext("2d");
  	  ctx.lineWidth = 4;
  	  ctx.moveTo(0, 0);
  	  ctx.lineTo(50, 50);
  	  ctx.moveTo(0, 50);
  	  ctx.lineTo(50, 0);
  	  ctx.stroke();

  	  var dt = event.dataTransfer;
  	  dt.setData('text/plain', 'Data to Drag');
  	  dt.effectAllowed = "none";
  	  dt.setDragImage(canvas, 25, 25);
  	}
  </script>

拖动效果

通过在dragstart事件监听器中设置 effectAllowed 属性,你可以为拖动源指定这三种操作中的某一种。

event.dataTransfer.effectAllowed = "copy";

  • none 禁止任何操作
  • copy 只进行复制操作
  • move 只进行移动操作
  • link 只进行连接操作
  • copyMove 只进行复制或移动操作
  • copyLink 只进行复制或连接操作
  • linkMove 只进行连接或移动操作
  • all 复制,移动,或者连接

注意:你必须从上面列出的值中选一个来用。如果不指定 effectAllowed属性,那么所有的操作都是允许的,就像指定了"all"一样。所以你没必要指定这个值,除非你真的想指定特定的操作。

在一次拖拽操作中,绑定在dragenter 事件以及dragover事件上的监听器能够检查effectAllowed属性来确认哪些操作是允许的。在这些事件中,可以设定一个相关的属性 dropEffect ,用以指定一种允许的操作。该属性的值可以是 none,copy,move或者link。不能是形如copyMove这样的复合值。

参考文章

拖放操作

HTML5离线缓存

概念

HTML5提供了一种离线应用缓存机制,使得网页应用可以离线使用,这种机制在移动端浏览器上支持度非常广,所有版本的android和ios浏览器都能很好的支持。我们可以放心的使用该特性来加速移动端页面的访问速度。

使用 HTML5,通过创建 cache manifest 文件,可以创建 web 应用的离线版本。引入了应用程序缓存,这意味着 web 应用可进行缓存,并可在没有因特网连接时进行访问。 应用程序缓存为应用带来三个优势:

  • 离线浏览 – 用户可在应用离线时使用它们
  • 速度 – 已缓存资源加载得更快
  • 减少服务器负载 – 浏览器将只从服务器下载更新过或更改过的资源。

其工作过程

maini

浏览器在解析HTML文档的过程中,遇到HTML标记的menifest属性时,就立即在后台开启一个新的进程下载需要离线缓存的资源。在下载离线缓存的过程中,会在ApplicationCache上触发一系列事件。

事件名称 含义 后续事件
checking checking事件是整个事件序列中的第一个,表示浏览器在首次下载缓存,或者在检查缓存是否有更新 noupdate, downloading, obsolete, error
noupdate noupdate事件是整个事件序列的最后一个,用来表示缓存没有更新
downloading 浏览器正在首次下载menifest中列出的离线资源,或者浏览器发现缓存有更新,并且正在下载该更新 progress, error, cached, updateready
progress 浏览器正在下载menifest中的离线资源,其中progressEvent对象中total表示需要下载的总资源的数量,loaded表示已经处理过的资源数量 progress, error, cached, updateready
cached 浏览器已经成功下载了menifest中的离线资源,应用已经被缓存
updateready 缓存有更新并且更新已经下载完成,这是可以使用ApplicationCache.swapCache()来将应用切换到最新缓存
obsolete 应用已经有离线缓存了,但menifest文件无法加载成功,则删除现有离线缓存
error 发生错误的情况比较多:可能是首次视图缓存离线文件时,发现menifest文件返回404;也可能是下载缓存过程中发生了fatal错误,比如超出了最大可用存储空间的限制,发现了header为Cache-Control: no-store的资源;还有可能是缓存在更新过程中发现menifest文件发生了变化

manifest

HTML5的离线存储是基于一个新建的.appcache文件的,通过这个文件上的解析清单离线存储资源,这些资源就会像cookie一样被存储了下来。之后当网络在处于离线状态下时,浏览器会通过被离线存储的数据进行页面展示。

在开始之前要先了解下manifest(即.appcache文件),上面的解析清单要怎么写。

manifest 文件是简单的文本文件,它告知浏览器被缓存的内容(以及不缓存的内容)。
manifest 文件可分为三个部分:

  • CACHE MANIFEST - 在此标题下列出的文件将在首次下载后进行缓存
  • NETWORK - 在此标题下列出的文件需要与服务器的连接,且不会被缓存
  • FALLBACK - 在此标题下列出的文件规定当页面无法访问时的回退页面(比如 404 页面)

在线的情况下,用户代理每次访问页面,都会去读一次manifest.如果发现其改变, 则重新加载全部清单中的资源。

看一个例子

CACHE MANIFEST
#version 1.0

CACHE:
/theme.css
/logo.gif
/main.js

NETWORK:
http://xxx/NotOffline
*

FALLBACK:
404.html

注意,真正运行或者测试离线Web应用程序的时候,需要对服务器进行配置,让服务器支持text/cache-manifest这个MIME类型-----在HTML5中规定manifest文件的MIME类型是text/cache-manifest例如对Apache服务器进行配置的时候,需要找到{apache_home}/conf/mime.types这个文件,并在文件最后添加如下的一行代码。

text/cache-manifest manifest

在HTML页面引用manifest文件

<html manifest="sample.appcache"

  • 第一行,CACHE MANIFEST,是必需的:

  • 上面的 manifest 文件列出了三个资源:一个 CSS 文件,一个 GIF 图像,以及一个 JavaScript 文件。当 manifest 文件加载后,浏览器会从网站的根目录下载这三个文件。然后,无论用户何时与因特网断开连接,这些资源依然是可用的。

  • NETWORK:白名单,使用通配符”*”. 则会进入白名单的open状态. 这种状态下.所有不在相关Cache区域出现的url都默认使用HTTP相关缓存头策略. 可以使用星号来指示所有其他资源/文件都需要因特网连接。
    下面的 NETWORK 小节规定文件 “login.asp” 永远不会被缓存,且离线时是不可用的:

NETWORK:
login.asp
  • FALLBACK:不能在线访问时使用备用资源文件。

更新缓存

一旦应用被缓存,它就会保持缓存直到发生下列情况:

  • 用户清空浏览器缓存
  • manifest 文件被修改
  • 由程序来更新应用缓存

核心是applicationCache对象,有个status属性,表示应用缓存的当前状态:

  • 0(UNCACHED) : 无缓存, 即没有与页面相关的应用缓存

  • (IDLE) : 闲置,即应用缓存未得到更新

  • (CHECKING) : 检查中,即正在下载描述文件并检查更新

  • (DOWNLOADING) : 下载中,即应用缓存正在下载描述文件中指定的资源

  • (UPDATEREADY) : 更新完成,所有资源都已下载完毕

缺点

  • 我们知道每次使用离线应用时,在有网络连接的情况下,浏览器都会逐字节的检查menifest文件是否有更新,而当menifest文件有更新时,就会重新下载menifest文件中列出的所有资源,资源下载成功后会触发updateready事件。这时离线应用本身并不会立即更新,而会在下次访问时才更新,这就是我们所说的二次更新。
window.addEventListener('load', function(e) {  
    window.applicationCache.addEventListener('updateready', function(e) { 
        //缓存更新完毕 
        if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {  
            //切换为最新缓存
            window.applicationCache.swapCache();  
            if (confirm('新版本已经更新完成,是否重新加载?')) {  
                window.location.reload();  
            }  
        }  
    }, false);  
}, false);  
  • 超出大小限制的问题:首次下载缓存时超出大小,所有资源都不会缓存,而是请求网络,应用功能正常。
    更新资源后超出大小,缓存不会更新,应用无法更新。
window.applicationCache.addEventListener('error',function(e){
    if(e.reason == 'quota'){
        //超出离线缓存大小限制
    }
});
  • 如果有修改的资源文件,必须通过修改manifest文件来属性被缓存的文件列表,只要manifest文件修改,那么所有缓存文件都会被重新拉取一次。更新是全局性的,无法单独更新某个无法单独更新某个文件。

  • 对于链接参数变化是敏感的,任何一个参数修改都会被更新缓存(重复婚车含参页面)index.html和index.html?renew=1会被认为是不同的文件,分别缓存。

注意

  • 在manifest中使用的相对路径,相对参照物为manifest文件

  • CACHE MANIFEST字符串应在第一行,且必不可少

  • 当一个资源被缓存后,该浏览器直接请求这个绝对路径也会访问缓存中的资源。

  • 站点中的其他页面即使没有设置manifest属性,请求的资源如果在缓存中也从缓存中访问

  • 当manifest文件发生改变时,资源请求本身也会触发更新。

注意: Application Cache被从Web 标准中移除

参考资料

HTML5离线存储原理及实现
HTML5离线存储
深入理解HTML5离线缓存机制
HTML5离线缓存(Application Cache)

SVG学习笔记之七

SVG图案

SVG图案一般用于SVG图形对象的填充fill或描边stroke。这个图形可以是一个SVG元素,也可以是位图图像,通过<pattern>元素在x轴或y轴方向以固定的间隔平铺。

你可以在<pattern>元素内定义图案,然后通过id引用。下面我们结合实例来讲解。

<svg width="660" height="220">
 <defs>
   <pattern id="pattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
     <circle cx="10" cy="10" r="10" stroke="none" fill="#393" />
   </pattern>
 </defs>

 <rect x="10" y="10" width="600" height="200" stroke="#630" stroke-width="5px" fill="url(#pattern)" />
</svg>

<defs>标签内定义图案。<pattern>元素中的内容直到引用的时候才会显示。<pattern>中定义一个id,方便在后面被矩形引用。<rect>元素使用图案作为填充,然后再使用纯色来作为描边。

<pattern>元素内,我们定义了一个半径为10(px)的绿色圆。cx和cy的值(分别为10px)设置了circle元素的初始圆的原点,或它的左上角。

<pattern>元素的属性

<pattern id="pattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">

x和y属性定义了图案的起点。这里我把它们都设置为0,这样在图案开始前就不会有偏移了。前面circle已经设置了中心在原点。设置x和y为0,使得圆形的中心和图案的起点对齐了。

width和height属性定义了重复图案的宽度和高度。这里我给它们设置了相同的尺寸,为圆的直径,所以图案是一个一个圆挨着重复的。

最后一个属性是patternUnits,定义pattern的坐标系统。它的可能值有两个,userSpaceOnUse和objectBoundingBox。

  • userSpaceOnUse:x、y、width和height表示的值都是当前用户坐标系统的值。也就是说,这些值没有缩放,都是绝对值。

  • objectBoundingBox(默认值):x、y、width和height表示的值都是外框的坐标系统(包裹pattern的元素)。也就是说,图案的单位进行了一个缩放,比如:pattern中为1的值,会变成和包裹元素的外框的width和height一样的大小。

只需要记住userSpaceOnUse不会对pattern的单位进行缩放,但是objectBoundingBox会。

 id="pattern" x=“0" y="0" width=“20" height="20" patternUnits="objectBoundingBox">

svg-03

因为我们把patternUnits的值改成了objectBoundingBox,而width和height都是20,会被放大,至于它们具体是按照600px(矩形的宽度)还是200px(矩形的高度)放大,需要根据方向是x还是y来确定(水平方向是x600,垂直方向是x200)。圆形图案下一次出现的位置是在矩形之外。

使用objectBoundingBox这个值时,width和height必须小于1.0,不然图案就只会出现一次。

<svg width="660" height="220">
 <defs>
   <pattern id="pattern" x="0" y="0" width="0.0333" height="0.1" patternUnits="objectBoundingBox">
     <circle cx="10" cy="10" r="10" stroke="none" fill="#393" />
   </pattern>
 </defs>

 <rect x="10" y="10" width="600" height="200" stroke="#630" stroke-width="5px" fill="url(#pattern)" />  
</svg>

怎么得出x和y为0.0333和0.1的?

20 (circle height) ÷ 200 (rectangle height) = 0.1
20 (circle width) ÷ 600 (rectangle width) = 0.0333

不过还是userSpaceOnUse更易于使用,而且大多数时候我们都是希望图案保留绝对值尺寸,但有时你可能想要pattern缩放,这个时候可能objectBoundingBox更好用

<patternContentUnits>属性

和patternUnits相似,patternContentUnits接受相同的两个值,userSpaceOnUse和objectBoundingBox。patternContentUnits属性定义了图案内容(注意是图案的内容,即中包裹的内容)的坐标系统,而不是图案本身。

  • userSpaceOnUse:(默认值)不缩放
  • objectBoundingBox:可缩放

代码看起来非常相似,但是因为内容进行了缩放,你需要减小坐标,以及圆的半径到小于1。这里我使用了0.125,也就是说圆的width和height都变成了0.25个单位。

因为图案的width和height都是0.25,也就是25%,所以可以确保图案的下一个重复的图案是和上一个图案紧紧相邻的。

图案不是正圆了,而是拉长了的椭圆。因为图案中定义的圆,通过一个半径定义,允许随着应用的矩形不同的x和y尺寸进行缩放(而矩形的宽度是高度的三倍),所以圆会在x方向上被拉长。要保持不变形,需要矩形的x和y的长度是一样的。

<rect x="10" y="10" width="600" height="600" stroke="#630" stroke-width="5px" fill="url(#pattern-2)" />

像以上代码这样改变就会出现正圆。其中0.125和0.25都是根据外层的svg元素的宽高来进行缩放。

xlink:href和patternTransform属性

  • xlink:href:你可以使用它来在SVG文档中引用另一个图案。
  • patternTransform:来将图案进行变换
<svg width="660" height="480">
  <defs>
    <pattern id="pattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
     <circle cx="10" cy="10" r="10" stroke="none" fill="#393" />
    </pattern>
  </defs>
<defs>
<!-- 引用另一个图案和patternTransform属性来将图案进行变换-->
    <pattern id="pattern-transformed" xlink:href="#pattern" patternTransform="rotate(10)">
    </pattern>
  </defs>
  <rect x="10" y="10" width="600" height="200" stroke="#630" stroke-width="5px" fill="url(#pattern)" />
  <rect x="10" y="250" width="600" height="200" stroke="#630" stroke-width="5px" fill="url(#pattern-transformed)" />
</svg>

第二个图案(id为pattern-transformed)通过xlink:href属性引用了第一个图案。它继承了第一个图案的所有内容。我还通过patternTransform属性添加了一个10度的旋转。

使用pattern描边

<svg width="660" height="220">
 <defs>
   <pattern id="pattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
     <circle cx="10" cy="10" r="10" stroke="none" fill="#393" />
   </pattern>
 </defs>

 <rect x="10" y="10" width="600" height="200" stroke="url(#pattern)" stroke-width="20px" fill="#630" />

早前我给矩形的填充引用了pattern。这里我把它作为描边,然后使用纯色来作填充。我把stroke-width设置为20px,这样圆圈才能完整显示出来。

pattern现在填充的是描边,而不是元素的背景。有一点需要注意的是,我们移动图案的位置只是在border之内移动。而不会移动整个border。

嵌套图案

<svg width="660" height="220">
  <defs>
    <pattern id="pattern-inner" x="2" y="2" width="6" height="6" patternUnits="userSpaceOnUse">
      <rect x="0" y="0" width="4" height="4" stroke="none" fill="#6a6" />
    </pattern>

    <pattern id="pattern-outer" x="13" y="13" width="50" height="50" patternUnits="userSpaceOnUse">
      <circle cx="22" cy="22" r="20" stroke="#900" stroke-width="4px" fill="url(#pattern-inner)" />
    </pattern>
  </defs>

<rect x="10" y="10" width="600" height="200" stroke="#630" fill="url(#pattern-outer)" />
</svg>

你也可以通过在一个图案中引用另一个图案来进行嵌套。这里我创建了两个图案,一个放里面,一个放外面。里面的图案是一系列重复的绿色方块,外面的图案是一系列重复的圆,带有暗红色的描边,并用第一个图案的内容进行填充。

你可以看到重复的圆形图案,中间填充的方块图案。嵌套图案外面的会剪切里面的图案。

参考文章

如何使用SVG图案
SVG图案:通过图片、属性和嵌套构建更复杂的图案

SVG学习笔记之一

SVG简介

矢量图和位图

  • 位图(BMP,PNG,JPG等)-----这些是基于颜色的描述,放大会失真。
  • 矢量图(SVG. AI等)------这些是基于数学的描述,图片不会失真。

一个简单的示例

<!-- 一个.svg文件-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="100" cy="50" r="40" stroke="black"
  stroke-width="2" fill="red" />
</svg>

SVG使用方式

  • 在浏览器直接打开
  • 在HTML中使用img标签引用
  • 直接在HTML中使用svg标签
  • 作为CSS背景

在HTML中使用img标签引用

<!-- 引入刚才画的svg文件 -->
<img src='simple.svg'>

直接在HTML中使用svg标签

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="100" cy="50" r="40" stroke="black"
  stroke-width="2" fill="red" />
</svg>

作为CSS背景

.bg {
     background: white url(simple.svg) repeat;
}

基本图形和属性

基本图形

  • rect ---- 矩形
  • circle ---- 圆形
  • ellipse ---- 椭圆
  • line ---- 线
  • polyline --- 折线
  • polygon ----多边形
  • path ---- 路径

基本属性

  • fill ---- 填充
  • stroke ---- 边框
  • transform ---- 旋转

rect----矩形

rect

  • 当我们没有给出ry的值的话,rx会直接使用rx的值。只用同时给出rx和ry的值话,才会使用rx,ry
  • rect 元素的 width 和 height 属性可定义矩形的高度和宽度
  • x 属性定义矩形的左侧位置(例如,x="0" 定义矩形到浏览器窗口左侧的距离是 0px)
  • y 属性定义矩形的顶端位置(例如,y="0" 定义矩形到浏览器窗口顶端的距离是 0px)
  • rx 和 ry 属性可使矩形产生圆角。

一个黑色的圆角矩形

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <rect width='300' height='300' x='50' y='50' rx='40' ry='40'/>
</svg>

circle----圆形

circle

  • cx和cy属性定义圆点的x和y坐标。如果省略cx和cy,圆的中心会被设置为(0, 0)
  • r属性定义圆的半径

一个红色的圆

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx='200' cy='200' r='100' fill='red' stroke='black' stroke-width='10'/>
</svg>

ellipse----椭圆

ellipse

  • cx属性定义的椭圆中心的x坐标
  • cy属性定义的椭圆中心的y坐标
  • rx属性定义的水平半径
  • ry属性定义的垂直半径

一个黑色的圆

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
 	<ellipse cx='300' cy='80' rx='100' ry='50'/>
</svg>

line----直线

  • x1 属性在 x 轴定义线条的开始
  • y1 属性在 y 轴定义线条的开始
  • x2 属性在 x 轴定义线条的结束
  • y2 属性在 y 轴定义线条的结束

画了一条红色的线段

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
 	<line x1='0' y1='0' x2='200' y2='200'  style='stroke:rgb(255,0,0);stroke-width:2'/>
</svg>

polyline----曲线

polyline

一条折线,填充为无,只是线段

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
 	<polyline points='20,20 40,56 60,40 80,120 120,140 200,160' style='stroke:red;stroke-width:3;fill:none'/>
</svg>

polygon----多边形

  • 用来创建含有不少于三个边的图形,多边形是由直线组成,其形状是"封闭"的(所有的线条 连接起来)。
  • points 属性定义多边形每个角的 x 和 y 坐标

一个五角星

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
 	<polygon points='100,10 40,180 190,60 10,60 160,180' style='stroke:red;stroke-width:3;fill:none'/>
</svg>

stroke属性

  • stroke----边框
  • stroke-width----边框的宽度
  • stroke-linecap----边框的端点的样式
  • stroke-dasharray----边框的虚线

一个无填充颜色,线段为虚线,线段颜色为红色

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
 	<polygon points='100,10 40,180 190,60 10,60 160,180' stroke-dasharray='5,5'
	stroke='red' stroke-width='4' fill='none'/>
</svg>
//端点为默认
stroke-linecap="butt"
//端点为圆形
stroke-linecap="round"
//端点我矩形
stroke-linecap="square"

图形的style属性

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <rect x="50" y="20" rx="20" ry="20" width="150" height="150"
  style="fill:red;stroke:black;stroke-width:5;opacity:0.5"/>
</svg>
  • style 属性用来定义 CSS 属性
  • CSS 的 fill-opacity 属性定义填充颜色透明度(合法的范围是:0 - 1)
  • CSS 的 stroke-opacity 属性定义笔触颜色的透明度(合法的范围是:0 - 1)
  • CSS 的 fill 属性定义矩形的填充颜色(rgb 值、颜色名或者十六进制值)
  • CSS 的 stroke-width 属性定义矩形边框的宽度
  • CSS 的 stroke 属性定义矩形边框的颜色

HTTP笔记之一

什么是HTTP协议

协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则,超文本传输协议(HTTP)是一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。目前我们使用的是HTTP/1.1 版本

 Web服务器,浏览器,代理服务器之间的关系

当我们打开浏览器,在地址栏中输入URL,然后我们就看到了网页。 原理是怎样的呢?

实际上我们输入URL后,我们的浏览器给Web服务器发送了一个Request, Web服务器接到Request后进行处理,生成相应的Response,然后发送给浏览器, 浏览器解析Response中的HTML,这样我们就看到了网页,过程如下图所示

webserve01

我们的Request 有可能是经过了代理服务器,最后才到达Web服务器的。过程如下图所示

webserve02

代理服务器就是网络信息的中转站,有什么功能呢?

  • 提高访问速度, 大多数的代理服务器都有缓存功能。
  • 突破限制, 也就是翻墙了
  • 隐藏身份。

HTTP协议的主要特点

  • 支持客户/服务器模式。
  • 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST等等。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  • 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  • 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  • 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

HTTP协议详解之请求

http请求由三部分组成,分别是:请求行、请求头部、请求正文

注意:请求头部和请求正文之间有一个空行

http_request

请求行

请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,
格式:Method Request-URI HTTP-Version CRLF

  • Method表示请求方法
  • Request-URI是一个统一资源标识符
  • HTTP-Version表示请求的HTTP协议版本
  • CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。

请求方法(所有方法全为大写)有多种,各个方法的解释如下:

  • GET 请求获取Request-URI所标识的资源
  • POST 在Request-URI所标识的资源后附加新的数据
  • HEAD 请求获取由Request-URI所标识的资源的响应消息报头
  • PUT 请求服务器存储一个资源,并用Request-URI作为其标识
  • DELETE 请求服务器删除Request-URI所标识的资源
  • TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断
  • CONNECT 保留将来使用
  • OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求

常见的HTTP请求头部属性

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
//keep-alive:当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,
//如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接
Cookie:BDUSS=lh3V2NLV3p6QkcyRFpuZDF5;BAIDUID=826:FG=1;H_PS_645EC=3e1R; sugstore=1
Host:www.baidu.com
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36
  • Accept:报文头属性告诉服务端,客户端接受什么类型的响应。

    • Accept:image/gif,表明客户端希望接受GIF图象格式的资源
    • Accept:text/html,表明客户端希望接受html文本
  • Accept-Charset:请求报头域用于指定客户端接受的字符集。

    • Accept-Charset:iso-8859-1,gb2312.如果在请求消息中没有设置这个域,缺省是任何字符集都可以接受。
  • Accept-Encoding:请求报头域类似于Accept,但是它是用于指定可接受的内容编码

    • Accept-Encoding:gzip.deflate :gzip是一种数据格式 ,默认且目前仅使用deflate算法压缩data部分,此法用于压缩传输
  • Accept-Language:它是用于指定一种自然语言

  • Accept-Language:zh-cn:表示客户端接受的语言是中文

  • Cache-Control:用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制)。

    • 请求时的缓存指令包括如下: no-cache(用于指示请求或响应消息不能缓存),no-store、max-age、max-stale、min-fresh、only-if-cached;
    • 响应时的缓存指令包括:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage.
  • Connection:普通报头域允许发送指定连接的选项。例如指定连接是连续,或者指定“close”选项,通知服务器,在响应完成后,关闭连接。

  • Referer:表示这个请求是从哪个URL过来的。

  • Authorization:请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证

  • Host: 发送请求时,该报头域是必需的,请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的-----请求的域名

  • User-Agent:告诉HTTP服务器, 客户端使用的操作系统和浏览器的名称和版本

  • Cookie:客户端发送,包含在HTTP请求的头部中。只有cookie的domain和path与请求的URL匹配才会发送这个cookie。

  • Content-Length:发送给HTTP服务器数据的长度。

常见的响应报头属性

HTTP/1.1 200 OK
Server: bfe/1.0.8.18
Date: Fri, 20 Jan 2017 01:55:16 GMT
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: private
Expires: Fri, 20 Jan 2017 01:55:16 GMT
Content-Encoding: gzip
X-UA-Compatible: IE=Edge,chrome=1
Strict-Transport-Security: max-age=172800
BDPAGETYPE: 2
BDQID: 0x85dc22040003b7e4
BDUSERID: 480680231
Set-Cookie: BDSVRTM=72; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=1420_21101; path=/; domain=.baidu.com
Set-Cookie: __bsi=17432393255636434607_00_0_I_R_75_0303_C02F_N_I_I_0; expires=Fri, 20-Jan-17 01:55:21 GMT; domain=www.baidu.com; path=/
  • Cache-Control:private------服务器响应的缓存的类型
  • Connection:keep-alive-------响应的连接方式
  • Content-Encoding:gzip-------返回的内容编码
  • Content-Type:text/html;charset=utf-8------返回内容的MIME类型
  • Content-Length------用于指明实体正文的长度,以字节方式存储的十进制数字来表示。
  • Server-----响应报头域包含了服务器用来处理请求的软件信息。与User-Agent请求报头域是相对应的
  • Expires------响应过期的日期和时间:为了让代理服务器或浏览器在一段时间以后更新缓存中(再次访问曾访问过的页面时,直接从缓存中加载,缩短响应时间和降低服务器负载)的页面,我们可以使用Expires报头域指定页面过期的时间。为了让浏览器不要缓存页面,我们也可以利用Expires实体报头域,设置为0。
  • Date-----服务器消息发出的时间
  • refresh------应用于重定向或一个新的资源被创造,在5秒之后重定向
  • Set-Cookie-----设置Http Cookie

参考文章

Http报文工作原理大解剖
HTTP协议详解(真的很经典)
HTTP 协议详解
HTTP客户端识别与cookie机制

Canvas 绘制时钟

Canvas 绘制时钟

课程链接

原理

clock

Math.sin(x)

x: 必需。一个以弧度表示的角。将角度乘以 0.017453293 (2PI/360)即可转换为弧度。

console.log(Math.sin(Math.PI)); //1

弧度与角度的关系

  • 弧度 = 弧长/半径
  • 一个圆的弧长 = 2πr
  • 1°的弧长 = 2πr/360
  • 1°的弧度 = 2π/360

因为一个时钟有12个刻度,每一刻度夹角为30°,这样我们就可以得到每一个刻度的x和y轴坐标。

// 画刻度与数字
function drawNumber() {
	// 从水平方向0°顺时针方向进行数时钟的
	var hourNumber = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2];
	// 设置字体大小和对齐方式
	context.font = 14*scale +'px Arial';
	context.textAlign = 'center';
	context.textBaseline = 'middle';
	// 求出每一个时钟对应的x和y坐标
	hourNumber.forEach(function(number, index){
		// 一个圆的弧度为2π,分成12份,那么每一个数字都占有弧度如下
		var numberRad = Math.PI*2 / 12 * index,
		// 每一个数字的的x和y坐标,把半径缩小一些让数字在圆内
		    numberX = Math.cos(numberRad)*(r-30*scale),
		    numberY = Math.sin(numberRad)*(r-30*scale);
		// 画文字
		context.fillText(number, numberX, numberY);
	});

	// 画刻度
	for (var i = 0; i < 60; i++) {
		var diaRad = Math.PI*2 / 60 * i,
		// 刻度比数字更靠近边缘
			diaX = Math.cos(diaRad)*(r-15*scale),
			diaY = Math.sin(diaRad)*(r-15*scale);
		// 重新开始话路径
		context.beginPath();
		// 判断如果是数字的刻度,颜色深一些,不是的话颜色前一些
		if (i % 5 === 0) {
			context.fillStyle = '#333';
		}else {
			context.fillStyle = '#999';
		}
		// 画刻度路径
		context.arc(diaX, diaY, 2*scale, 0, 2*Math.PI, false);
		// 填充刻度
		context.fill();
		// 关闭路径
		context.closePath();
	}
}

此时当我们话时针的时候,此时的弧度是小时的弧度加上分钟的弧度。

// 画小时刻度
function drawHour(hour, minute) {
	// 把前面的状态存档,开始这个状态
	context.save();
	// 开始新路径
	context.beginPath();
	// 获取弧度
	var hourRad = 2 * Math.PI / 12 * hour + 2 * Math.PI / 12 / 60 * minute;
	// 旋转弧度
	context.rotate(hourRad);
	context.lineWidth = 6;
	// 设置线段的端点的样式,为圆的
	context.lineCap = 'round';
	// 线段的开始的坐标
	context.moveTo(0, 10*scale);
	context.lineTo(0, -r / 2);
	context.stroke();
	// 关闭路径
	context.closePath();
	// 恢复到上一个状态
	context.restore();
}

注意,保存状态和恢复状态的使用。

Canvas坐标轴

x轴坐标的正方向是向右
y轴坐标的正方向是向下

SVG学习笔记之十二

SVG SMIL animation概览

SMIL Synchronized Multimedia Integration Language(同步多媒体集成语言)的首字母缩写简称,是有标准的。SVG动画就是基于这种语言。

SMIL的特性有以下几种:

  • 动画元素的数值属性(X,Y)
  • 动画属性的变换(平移或者旋转)
  • 动画的颜色属性
  • 沿着运动路径运动

SVG的动画元素是和SMIL开发组合作开发的。SMIL开发组和SVG开发组合作开发了SMIL动画规范,在规范中制定了一个基本的XML动画特征集合。SVG吸收了SMIL动画规范当中的动画优点,并提供了一些SVG继承实现。

支持情况

IE浏览器(包括IE11)是不支持的。

SVG animation元素分类

  • <set>
  • <animate>
  • <animateColor>------animate可以实现其功能与效果被废弃
  • <animateTransform>
  • <animateMotion>

定位动画目标

  • 利用xlink:href进行定位动画目标
    <animate xlink:href="url(#rect1)"></animate>

  • 被包含在目标元素里
    <rect><animate></animate></rect>

set

set意思设置,此元素没有动画效果,但是实现基本的延迟功能。就是指:可以在特定时间之后修改某个属性值(也可以是CSS属性值)。即没有过渡效果,只是一瞬间改变某个属性值。

<svg width="100%" height="100%">
    <rect x="100" y="100" width="100" height="100" fill="red">
        <set
           attributeType="XML"
           attributeName="x"
           from="100"
           to="500"
           begin="3s" 
        ></set>
    </rect>
</svg>
  • begin="3s" 这个代表在3s时间后执行属性的变化。

animate

基础动画元素。实现单属性的动画过渡效果。

<svg width="100%" height="100%">
    <rect x="100" y="100" width="100" height="100" fill="red">
        <animate
          attributeType="XML"
          attributeName="x"
          from="100"
          to="500"
          dur="3s"
          fill="freeze"
        ></animate>
    </rect>
</svg>
  • attributeType这个可以改变css和XML里面的属性,如果没指定的话,浏览器会先到样式表中寻找相对应的属性名,如果没有找到话,就会到XML中寻找。

  • attributeName动画要作用的属性,例如上面的这个例子就是改变XML中的x值。

  • from和to代表起始位置和结束位置

  • fill有两个值。

    • freeze代表这从100到500的位置就停止动画不回到起点位置。
    • remove(默认值)代表从100到500后,再回到原点100,即移除to设置的值。

animateTransform

这里的transform变换与CSS3的transform变换类似,即变换动画。

<svg width="1000" height="800">
    <rect x="300" y="300" width="100" height="100" fill="red" transform="rotate(0)">
        <animateTransform
           attributeType="XML"
           attributeName="transform"
           type="rotate"
           from="0"
           to="360" 
           dur="3s"
        ></animateTransform>
    </rect>
</svg>
  • type 表示要改变的变换的属性

animateMotion

动画沿着特定的路径。这个是路径动画。

<svg width="360" height="200" xmlns="http://www.w3.org/2000/svg">
  <text font-family="microsoft yahei" font-size="40" x="0" y="0" fill="#cd0000"><animateMotion path="M10,80 q100,120 120,20 q140,-50 160,0" begin="0s" dur="3s" rotate="auto" repeatCount="indefinite"/>
  </text>
  <path d="M10,80 q100,120 120,20 q140,-50 160,0" stroke="#cd0000" stroke-width="2" fill="none" />
</svg>

动画还可以叠加

<svg width="100%" height="100%">
    <rect x="100" y="100" width="100" height="100" fill="red">
        <animate
          attributeType="XML"
          attributeName="x"
          from="100"
          to="500"
          dur="3s"
          fill="freeze"
        ></animate>
        <animate
          attributeType="XML"
          attributeName="fill"
          from="red"
          to="blue"
          dur="3s"
          fill="freeze"
        ></animate>
    </rect>
</svg>
  • repeatCount:这个动画属性代表动画循环多少次,无限次是infinite

SVG animation参数

attributeName

attributeName = <attributeName> 可以XML的属性或者CSS的属性

attributeType

attributeType = “CSS | XML | auto” attributeType支持三个固定参数,CSS/XML/auto. 用来表明attributeName属性值的列表。x, y以及transform就属于XML, opacity就属于CSS. auto为默认值,自动判别的意思(实际上是先当成CSS处理,如果发现不认识,直接XML类别处理)。

from, to, by, values

  • from = ““ 动画的起始值。
  • to = ““ 指定动画的结束值。
  • by = ““ 动画的相对变化值。
  • values = ““ 用分号分隔的一个或多个值,可以看出是动画的多个关键值点。

from, to, by, values 这四则的制约关系

  • 如果动画的起始值与元素的默认值是一样的,from参数可以省略。
  • (不考虑values)to,by两个参数至少需要有一个出现。否则动画效果没有。
    • to表示绝对值,by表示相对值。拿位移距离,如果from是100, to值为160则表示移动到160这个位置
    • 如果by值是160,则表示移动到100+160=260这个位置。
  • 果to,by同时出现,则by打酱油,只识别to
  • 如果to,by,values都没设置,自然没动画效果。如果任意(包括from)一个属性的值不合法,规范上说是 没有动画效果。
  • values可以是一个值或多值。当values值设置并能识别时候,from, to, by的值都会被忽略。我们实现动画,不可能就是单纯的从a位置到b位置,有时候,需要去c位置过渡下。此时,实际上有3个动画关键点。而from, to/by只能驾驭两个,此时就是values大显身手的时候了!
<svg width="320" height="200" xmlns="http://www.w3.org/2000/svg">
    <text font-family="microsoft yahei" font-size="120" y="150" x="160"><animate attributeName="x" values="160;40;160" dur="3s" repeatCount="indefinite" />
    </text>
</svg>

也就是from-to动画、from-by动画、to动画、by动画以及values动画

dur

dur属性值:常规时间值 | "indefinite".

常规时间值”就是3s之类的正常值;"indefinite"指事件无限。试想下,动画时间无限,实际上就是动画压根不执行的意思。因此,设置为"indefinite"跟没有dur是一个意思,与dur解析异常一个意思。

calcMode, keyTimes, keySplines

这几个参数是控制动画先快还是先慢类似这样作用的。

calcMode属性支持4个值:discrete | linear | paced | spline. 中文意思分别是:“离散”|“线性”|“踏步”|“样条”。

  • discrete------from值直接跳到to值。
  • linear-----animateMotion元素以外元素的calcMode默认值。动画从头到尾的速率都是一致的。
  • paced-----通过插值让动画的变化步调平稳均匀。仅支持线性数值区域内的值,这样点之间“距离”的概念才能被计算(如position, width, height等)。如果”paced“指定,任何keyTimes或keySplines值都会打酱油。
    *spline-----插值定义贝塞尔曲线。spline点的定义在keyTimes属性中,每个时间间隔控制点由keySplines定义。

keyTimes = <list>
跟上面提到的类似,都是分号分隔一组值。keyTimes总名字上看是关键时间点的意思,大致就是这个意思。前面提到过values也是多值,这里有一些约定的规则:首先,keyTimes值的数目要和values一致,如果是from/to/by动画,keyTimes就必须有两个值。然后对于linear和spline动画,第一个数字要是0, 最后一个是1。 最后,每个连续的时间值必须比它前面的值大或者相等。

paced模式下,keyTimes会被忽略;keyTimes定义错误,也会被忽略;dur为indefinite也会被忽略。

keySplines = <list>
keySplines表示的是与keyTimes相关联的一组贝塞尔控制点(默认0 0 1 1)。每个控制点使用4个浮点值表示:x1 y1 x2 y2. 只有模式是spline时候这个参数才有用,也是分号分隔,值范围0~1,总是比keyTimes少一个值。

如果keySplines值不合法或个数不对,是没有动画效果的。

repeatCount和repeatDur

repeatCount表示动画执行次数,可以是合法数值或者”indefinite“。

repeatDur定义重复动画的总时间。可以是普通时间值或者”indefinite。

<animate attributeName="x" to="60" dur="3s" repeatCount="indefinite" repeatDur="10s" />

动画只玩执行完整3个 + 一个1/3个动画。因为repeat总时间就10s而已。

fill属性

  • freeze代表这从100到500的位置就停止动画不回到起点位置。
  • remove(默认值)代表从100到500后,再回到原点100,即移除to设置的值。

accumulate, additive

accumulate是累积的意思。支持参数有:none | sum. 默认值是none. 如果值是sum表示动画结束时候的位置作为下次动画的起始位置。

additive控制动画是否附加。支持参数有:replace | sum. 默认值是replace. 如果值是sum表示动画的基础知识会附加到其他低优先级的动画上,

<animateTransform attributeName="transform" type="scale" from="1" to="3" dur="10s" repeatCount="indefinite" additive="sum"/>
<animateTransform attributeName="transform" type="rotate" from="0 30 20" to="360 30 20" dur="10s" fill="freeze" repeatCount="indefinite" additive="sum"/>;

restart

restart这个属性诞生的背景如下:很多动画呢,其触发可能与事件相关,例如,点击某圆圈,马儿就跑。而且,似乎没点一次,马儿就跑一下。现在,存在这种情况,希望马儿只跑一次,之后在点击就没有反应。这种需求的出现迫使restart参数的出现。支持的参数有:always | whenNotActive | never.

  • always是默认值,表示总是,也就是点一次圈圈,马儿跑一下。
  • whenNotActive表示动画正在进行的时候,是不能重启动画的。
  • never表示动画是一波流

min, max

min/max表示动画执行最短和最长时间。支持参数为时间值和"media"(媒介元素有效), max还支持indefinite.

动画的暂停与播放

// svg指当前svg DOM元素
// 暂停
svg.pauseAnimations();

// 重启动
svg.unpauseAnimations()

参考文章

超级强大的SVG SMIL animation动画详解
svg动画

Canvas学习笔记之三

绘制图像

在HTML5中,不仅可以使用Canvas API来绘制图形,还可以读取磁盘或网络中的图形文件,然后使用Canvas API将该图像绘制在画布中。

绘制图像时,需要使用drawImage方法,该方法的定义如下:

content.drawImage(image, x, y)
content.drawImage(image, x, y, w, h)
content.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

第一种方法---content.drawImage(image, x, y)

  • image是一个Image对象,用该对象装载图像文件
  • x和y为绘制时,该图像在画布中的起始坐标
  • 该方法绘制出来的图像大小与原图大小是相同的。

第一种方法---content.drawImage(image, x, y, w, h)

  • 前三个参数跟第一种方法的参数是一样的。
  • w和h是绘制时图像的宽度和高度。
  • 该方法可以用来进行图像的缩放

第三种方法---content.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

  • sx和sy:分别表示源图像的被复制区域在画布中的起始横坐标和起始纵坐标
  • sw和sh:分别表示被复制区域的宽度和高度
  • dx和dy:分别表示复制后的目标图像在画布的起始横坐标和起始纵坐标
  • dw和dh:分别表示复制后的目标图像的宽度和高度
  • 该方法可以只复制图像的局部,只要把sx和sy设为局部区域的起始点坐标,将sw和sh设为局部区域的宽度和高度。
  • 该方法也可以用来将源图像进行缩放,只有将dw和dh设置为缩放后的宽度和高度就可以了。

同一个图像文件绘制在画布中不同的位置上例子

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
   console.log("hello");
  var content =canvas.getContext('2d');
  content.fillStyle = '#eef';
  content.fillRect(0, 0, 400, 300);
  // 填充图形
  var image = new Image();
  image.src = '2.jpg';
  // 图形加载完成
  image.onload = function() {
    drawImg(content, image);
  }
}
function drawImg(content, image) {
  for (var i = 0; i < 7; i++) {
    content.drawImage(image, 0 + i*50, 0 + i*25, 100, 100);
  }

}
window.onload = function() {
  draw();
}

一个局部放大的例子

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
   console.log("hello");
  var content =canvas.getContext('2d');
  content.fillStyle = '#eef';
  content.fillRect(0, 0, 400, 300);
  // 填充图形
  var image = new Image();
  image.src = '2.jpg';
  // 图形加载完成
  image.onload = function() {
    drawImg(content, image);
  }
}
function drawImg(content, image) {
// 首次调用该方法绘制原始图像
 content.drawImage(image, 0, 0, 100, 100);
 // 绘制将局部区域进行放大后图像
 content.drawImage(image, 23, 5, 57, 80, 110, 0, 100, 100);
 // 要复制的区域:23, 5为画布上的坐标,57, 80要复制的宽高
 // 复制后的区域:110, 0为画布上的坐标,100,100复制后的宽高

}
window.onload = function() {
  draw();
}

图像平铺

图像平铺:按一定比例缩小后的图像将画布填满,有两种方法可以实现该技术,一种是使用drawImage方法,还有一种是图形上下文对象的createPatter方法。

第一种:drawImage方法

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  content.fillStyle = '#eef';
  content.fillRect(0, 0, 400, 300);
  // 填充图形
  var image = new Image();
  image.src = '2.jpg';
  // 图形加载完成
  image.onload = function() {

    drawImg(canvas, content, image);
  }
}
function drawImg(canvas, content, image) {
  // 平铺比例
  var scale = 8;
  // 缩小后图像的宽度
  var sx = image.width/scale;
  // 缩小后图形的高度
  var sy = image.height/scale;
  // 平铺横向的宽度
  var numberX = canvas.width/sx;
  var numberY = canvas.height/sy;
  console.log(numberX);
  console.log(numberY);
  // 进行循环平铺
  for (var i = 0; i < numberX; i++) {
    // 先对X轴进行循环,再对Y轴进行循环
    for (var j = 0; j < numberY; j++) {
        content.drawImage(image, i*sx, j*sy, sx, sy);
    }
  }

}
window.onload = function() {
  draw();
}

该方法需要使用几个变量以及循环进行处理,处理方法相对来说复杂一点。

第二种方法:createPattern方法

该方法定义:content.createPattern(image, type);

该方法使用两个参数:image参数为要平铺的图像,type参数的值必须是下面的字符串值之一。

  • no-repeat: 不平铺
  • repeat-x:横方向平铺
  • repeat-y:纵方向平铺
  • repeat:全方向平铺

创建了image对象并指定图像文件后,使用createattern方法创建填充样式,然后将该样式指定给图形上下文对象的fillStyle属性。最后再填充画布,就可以看到重复填充的效果了。

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  // 填充图形
  var image = new Image();
  image.src = '3.png';
  // 图形加载完成
  image.onload = function() {
    // 创建填充样式,全方向平铺
    var ptrn = content.createPattern(image, 'repeat');
    // 指定填充样式
    content.fillStyle = ptrn;
    content.fillRect(0, 0, 400, 300);
  }
}

window.onload = function() {
  draw();
}

图形剪裁

Canvas API的图像剪裁功能是指,在画布内使用路径,只绘制该路径所包括区域内的图像,不绘制路径外部的图像。

使用图形上下文对象的不带参数的clip方法来实现Canvas元素的图像剪裁功能。该方法使用路径来对Canvas画布设置一个剪裁区域,因此,必须先创建好路径。路径创建完成后,调用clip方法设置剪裁区域。

一个五角星的剪裁的例子

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  // 填充渐变
  var gr = content.createLinearGradient(0, 400,300,0);
  gr.addColorStop(0, 'rgb(255, 255, 0)');
  gr.addColorStop(1, 'rgb(0, 255, 255)');
  content.fillStyle = gr;
  content.fillRect(0, 0, 400, 300);
  // 填充图形
  var image = new Image();
  // 图形加载完成
  image.src = '2.jpg';
  image.onload = function() {
      drawImg(content, image);
  }

}
function drawImg(content, image) {
    create5StarClip(content);
    content.drawImage(image, -50, -150, 300, 300);
}
function create5StarClip(content) {
  var n = 0,
      dx =100,
      dy = 0,
      s = 150;
  // 创建路径
  content.beginPath();
  content.translate(100, 150);
  var x = Math.sin(0);
  var y = Math.cos(0);
  var dig = Math.PI/5*4;
  for (var i = 0; i < 5; i++) {
    var x = Math.sin(i*dig);
    var y = Math.cos(i*dig);  
    content.lineTo(dx + x*s, dy + y*s);
  }
  content.clip();
}
window.onload = function() {
  draw();
}

clip

先创建裁剪区域, 再绘制图像(之后绘制的图形都会采用这个裁剪区域,要取消这个裁剪区域就需要用到保存恢复状态

像素处理

getImageData方法

imagedata

使用Canvas API能够获取图像中的每一个像素,然后得到该像素颜色的RGB或者RGBA值。使用图形上下文对象的getImageData方法来获取图像中的像素,该方法定义如下:

var imageData = content.getImageDate(sx, sy, sw, sh)

  • sx和sy分别代表所获取区域的起点横坐标和纵坐标
  • sw和sh分别表示所获取区域的宽度和高度

imageData变量是一个CanvasPixelArray对象,具有height,width,data等属性。data属性是一个保存像素数据的数组。顺序是所取像素范围的从左到右,从上到下,数组的元素是(所有图形,包括图片,和绘制的图形)每个像素的rgba[r1,g1,b1,a1,r2,g2,b2,a2...]。其中r1,g1,b1,a1为第一个像素的红色值,绿色值,蓝色值,透明度值。依次类推,data.length为取得像素数。

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  // 填充渐变
  var gr = content.createLinearGradient(0, 400,300,0);
  content.fillStyle = 'red';
  content.fillRect(0, 0, 400, 300);
  // 填充图形
  var image = new Image();
  // 图形加载完成
  image.src = '2.jpg';
  image.onload = function() {
      content.drawImage(image, 0, 0, 200, 200);
    var  images = content.getImageData(0, 0, 400, 300);
    console.log(images.data.length);
  }
}
window.onload = function() {
  draw();
}

"Unable to get image data from canvas because the canvas has been tainted by cross-origin data"问题产生原因及解决办法

  • 问题描述

一、问题描述:

在支持html5的浏览器中运行javascript脚本,脚本主要是操作网页上的标签canvas,出错的操作为, getImageData(img,……),

chrome 下出错信息为:"Unable to get image data from canvas because the canvas has been tainted by cross-origin data",

fireFox 下出错信息为: "Security error" code: "1000"

var imgData=ctx.getImageData(0,0,img.width,img.height); //出错行

  • 解答

在网上搜索时发现大部分都说的是,getImageData这个函数,必须在服务器端运行,如果没有服务器环境(比如,只是一个本地的 html网页,操作本地的一张图片),就会报"Unable to get image data from canvas because the canvas has been tainted by cross-origin data"错误。

为了阻止欺骗,浏览器会追踪 image data。当你把一个“跟canvas的域不同的”图片放到canvas上,这个canvas就成为 “tainted”(被污染的,脏的),浏览器就不让你操作该canvas 的任何像素。这对于阻止多种类型的XSS/CSRF攻击(两种典型的跨站攻击)是非常有用的。

重要的在于网页所在域与图片所在的域是在同一个域中

实验证明imagedata取的是canvas所在范围画的图形,不止是图片不会取该区域内是空白的canvas的像素

PutImageData方法

putimage

一个Canvas API将图像进行反显操作的例子。在该例子中,在得到像素数组后,将该数组中的每一个像素的颜色进行了反显操作,然后保存回像数组,最后使用图像上下文对象的PutImageData方法将反显操作后的图像重新绘制在画布上。

content.PutImageData(imageData, dx, dy[ ,dirtyX, dirtyY, dirtyWidth, dirtyHeight])

  • imageData前面的像素数组
  • dx和dy分别代表重绘图像的起始横坐标和纵坐标
  • dirtyX, dirtyY, dirtyWidth, dirtyHeight为四个可选参数,给出一个矩形的起点横坐标和纵坐标,宽度和高度,如果加上这四个参数,则只绘制像素数组中的矩形范围内的图像。
function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  // 填充渐变
  var gr = content.createLinearGradient(0, 400,300,0);
  content.fillStyle = 'red';
  content.fillRect(0, 0, 400, 300);
  // 填充图形
  var image = new Image();
  // 图形加载完成
  image.src = '2.jpg';
  image.onload = function() {
       content.drawImage(image, 0, 0, 200, 200);
    var  imagedata = content.getImageData(0, 0, 400, 300);
   for (var i = 0, len = imagedata.data.length; i < len; i += 4) {
      imagedata.data[i + 0] = 255 - imagedata.data[i + 0]; //red;
      imagedata.data[i + 1] = 255 - imagedata.data[i + 1]; //green
      imagedata.data[i + 2] = 255 - imagedata.data[i + 2]; //blue
    //imagedata.data[i + 3] = 255 - imagedata.data[i + 3]; //a
   }
   content.putImageData(imagedata, 0, 0);
  }
}
window.onload = function() {
  draw();
}

开始的时候

canvas06

反显操作之后

canavs07

HTML5的WebSocket

什么是socket?什么是websocket?两者有什么区别?websocket是仅仅将socket的概念移植到浏览器中的实现吗?

我们知道,在网络中的两个应用程序(进程)需要全双工相互通信(全双工即双方可同时向对方发送消息),需要用到的就是socket,它能够提供端对端通信,对于程序员来讲,他只需要在某个应用程序的一端(暂且称之为客户端)创建一个socket实例并且提供它所要连接一端(暂且称之为服务端)的IP地址和端口,而另外一端(服务端)创建另一个socket并绑定本地端口进行监听,然后客户端进行连接服务端,服务端接受连接之后双方建立了一个端对端的TCP连接,在该连接上就可以双向通讯了,而且一旦建立这个连接之后,通信双方就没有客户端服务端之分了,提供的就是端对端通信了。我们可以采取这种方式构建一个桌面版的im程序,让不同主机上的用户发送消息。从本质上来说,socket并不是一个新的协议,它只是为了便于程序员进行网络编程而对tcp/ip协议族通信机制的一种封装。

websocket是html5规范中的一个部分,它借鉴了socket这种**,为web应用程序客户端和服务端之间(注意是客户端服务端)提供了一种全双工通信机制。同时,它又是一种新的应用层协议,websocket协议是为了提供web应用程序和服务端全双工通信而专门制定的一种应用层协议,通常它表示为:ws://echo.websocket.org/?encoding=text HTTP/1.1,可以看到除了前面的协议名和http不同之外,它的表示地址就是传统的url地址。

可以看到,websocket并不是简单地将socket这一概念在浏览器环境中的移植。

websocket的通信原理和机制

既然是基于浏览器端的web技术,那么它的通信肯定少不了http,websocket本身虽然也是一种新的应用层协议,但是它也不能够脱离http而单独存在。具体来讲,我们在客户端构建一个websocket实例,并且为它绑定一个需要连接到的服务器地址,当客户端连接服务端的时候,会向服务端发送一个类似下面的http报文

websocket01

可以看到,这是一个http get请求报文,注意该报文中有一个upgrade首部,它的作用是告诉服务端需要将通信协议切换到websocket,如果服务端支持websocket协议,那么它就会将自己的通信协议切换到websocket,同时发给客户端类似于以下的一个响应报文头

websocket02

返回的状态码为101,表示同意客户端协议转换请求,并将它转换为websocket协议。以上过程都是利用http通信完成的,称之为websocket协议握手(websocket Protocol handshake),进过这握手之后,客户端和服务端就建立了websocket连接,以后的通信走的都是websocket协议了。所以总结为websocket握手需要借助于http协议,建立连接后通信过程使用websocket协议。同时需要了解的是,该websocket连接还是基于我们刚才发起http连接的那个TCP连接。一旦建立连接之后,我们就可以进行数据传输了,websocket提供两种数据传输:文本数据和二进制数据。

基于以上分析,我们可以看到,websocket能够提供低延迟,高性能的客户端与服务端的双向数据通信。它颠覆了之前web开发的请求处理响应模式,并且提供了一种真正意义上的客户端请求,服务器推送数据的模式,特别适合实时数据交互应用开发。

在websocket之前,我们在web上要得到实时数据交互都采用了哪些方式?

  • 定期轮询的方式:
  • comet技术

当然并不是说这些技术没有用,就算websocket已经作为规范被提出并实现,但是对于老式浏览器,我们依然需要将它降级为以上方式来实现实时交互和服务端数据推送。

什么是WebSocket 的支持情况

WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。WebSocket目前由W3C进行标准化。WebSocket已经受到Firefox 4、Chrome 4、Opera 10.70以及Safari 5等浏览器的支持。

WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。

WebSocket API的用法

浏览器支持情况检测

function loadDemo() {  
    if (window.WebSocket) {  
        //supported  
    } else {  
        // not supported  
    }  
}  

WebSocket对象的创建和服务器连接

要连接通信端点,只需要创建一个新的WebSocket实例,并提供希望连接的对端URL。ws://和wss://前缀分别表示WebSocket连接和安全的WebSocket连接。

url = "ws://localhost:8080/echo";  
w = new WebSocket(url);  

建立WebSocket连接时,可以列出Web应用能够使用的协议。WebSocket构造函数的第二个参数既可以是字符串,也可以是字符串组。

w = new WebSocket(url, ["proto1", "proto2"]);  

假设proto1和proto2是定义明确、可能已注册且标准化的协议名称,它们能够同时为客户端和服务器端所理解。服务器会从列表中选择首选协议。

onopen = function(e) {  
    //确定服务器选择的协议  
    log(e.target.protocol);  
}  

添加事件监听器

WebSocket编程遵循异步编程模型;打开socket后,只需等待事件发生,而不需要主动向服务器轮询,所以需要在WebSocket对象中添加回调函数来监听事件。
WebSocket对象有四个事件:open、close、message和error

w.onopen = function() {  
    log("open");  
    w.send("send message");  
}  
w.onmessage = function(e) {  
    log(e.data);  
}  
w.onclose = function(e) {  
    log("closed");  
}  
w.onerror = function(e) {  
    log("error");  
}  

发送消息

当socket处于打开状态(即onopen之后,onclose之前),可以用send方法来发送消息。消息发送完,可以调用close方法来终止连接,也可以不这么做,让其保持打开状态。

w.send();

你可能想测算在调用Send()函数之前,有多少数据备份在发送缓冲区中。bufferAmount属性表示已在WebSocket上发送但尚未写入网络的字节数。它对于调节发送速率很有用。

document.getElementById("sendButton").onclick = function() {  
    if (w.bufferedAmount < bufferThreshold) {  
        w.send(document.getElementById("inputMessage").value);  
    }  
}  

WebSocket API支持以二进制数据的形式发送Blob和ArrayBuffer实例

var a = new Uint8Array([8, 6, 7, 5, 3, 0, 9]);  
w.send(a.buffer); 

比较有名的websocket的库

socket.io
sockjs

参考文章

认识HTML5的WebSocket
html5-websocket初探
HTML5学习笔记(七)-WebSockets API

绘制五角星和炫丽的倒计时效果的笔记

绘制倒计时效果

课程链接:炫丽的倒计时效果Canvas绘图与动画效果

绘制思路

  • 获取当前时间的毫秒数与结束时间的毫秒数的差值,分别转为小时,分钟和秒数。

  • 分别把小时,分钟和秒数(设计的最大的差值为99:59:59,即小时位数最多为两位数)的数字,用canvas的小球绘制出数字。

  • 然后在每隔一段时间,就更新当前时间,即重新获取当前时间,与结束时间比较差值,清除上一次canvas画布上面绘制出的时间,重新绘制这一次的查差值的时间。这样就形成了倒计时了。

  • 对于每一次时间改变的动画效果,通过与上一次的时间(小时,分钟和秒数的数字比较)不同的就绘制出该数字,然后让该数字的小球做自由落体运动。

其中对于数字有小球绘制的思路如下:

digital

先把所需要的数字0-9以及冒号封装在一个数组中,该数组的结构如下:

digital =
    [
        [
            [0,0,1,1,1,0,0],
            [0,1,1,0,1,1,0],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [1,1,0,0,0,1,1],
            [0,1,1,0,1,1,0],
            [0,0,1,1,1,0,0]
        ],//0
//...省略1-9
        [
            [0,0,0,0],
            [0,0,0,0],
            [0,1,1,0],
            [0,1,1,0],
            [0,0,0,0],
            [0,0,0,0],
            [0,1,1,0],
            [0,1,1,0],
            [0,0,0,0],
            [0,0,0,0]
        ]//:绘制的是冒号
    ];

该数组是一个二维数组,其中1代表是一个实心的圆,0代表一个空心的圆,从而绘制成数字。

小球的位置:

  • 该小球的起始坐标都是从0,0点开始的

  • ballR为小球的半径,i代表纵向,j代表横向。此时第i行,j列的小球的位置如下

  • X轴:x + j2(ballR+1) + (ballR+1) ----加1是为了让小球之间有一定的间隔

  • Y轴: y + i2(ballR+1) + (ballR+1)

绘制数字小球的如下:

// 绘制数字,x和y代表数字的起点坐标
	function renderDigital(x, y, number) {
		// 字的圆形实体的颜色
		context.fillStyle = 'rgb(0, 102, 153)';
		var digitalLen = digital[number].length;
		// 对digital
		for (var i = 0; i < digitalLen; i++) {
			for (var j = 0, numberLen = digital[number][i].length; j < numberLen ; j++) {
				// 如果是1的话,就画实心圆
				if (digital[number][i][j] === 1) {
					context.beginPath();
					// 画圆
					context.arc(x + j*2*(ballR+1) + (ballR+1), y + i*2*(ballR+1) + (ballR+1), ballR, 0, Math.PI*2, true);
					context.closePath();
					context.fill();
				}	
			}
		}
	}

小球的弹跳的原理:

  • 定义一个小球的加速度,X轴方向的初速度和Y轴方向的初速度。

  • 然后每隔一段时间,改变小球在canvas的坐标轴上的位置,其X轴的位置为X轴的初速度累加。Y轴方向同样如此。

  • 而且Y轴的的速度一直在增加,其速度Y轴的初速度累加加速度。

代码如下:

ball = {
			x: 500, //小球的X坐标
			y: 50, //小球的Y坐标
			r: 20, //小球的半径
			g: 1, //加速度
			vx: -4, //x轴的方向的速度
			vy: 0, //y轴的方向速度
			color: '#005588'
};

//小球的位置的更新

function update() {
ball.x += ball.vx; //x轴的距离为开始的距离+小球的速度
ball.y += ball.vy; //y轴的距离为开始的距离+小球的速度
ball.vy += ball.g; //且Y轴的速度变化为开始的速度+加速度

// 对小球的边界区域进行判断,使得小球在区域内来回反弹
// 对下边界进行判断
if (ball.y >= ch - ball.r) {
	// 当他超出y轴的边界后,回到最低点
	ball.y = ch - ball.r;
	// 其y轴的速度,变成开始速度的方向方向
	ball.vy = -ball.vy*0.7
	// 0.5是摩擦系数,因为小球不是每次都弹到最高点
	// 会慢慢变为0;
}

// 上边界的判断
if (ball.y <= 0) {
	ball.y = 0;
	ball.vy = -ball.vy*0.7;
}

//左边界的判断
if (ball.x <= 0) {
	ball.x = 0;
	ball.vx = -ball.vx*0.7;
}

// 右边界的判断
if (ball.x >= cw-ball.r) {
	ball.x = cw-ball.r;
	ball.vx = -ball.vx*0.7;
}

数字的之间的距离的绘制

小时上面的两位数:

  • 第一位是从0,0点开始绘制的

  • 第二位是从第一位后面开始绘制,其一个数字所占得宽度,是上面数字的数组中定义的宽度,一共7列,所以距离是7*2(R+1)+(R+1)。其中最后加上(R+1)是为了数字之间间隔一定的距离

  • 后面的依次类推,注意冒号是4列。

自适应屏幕

  • 此时获取屏幕的可视宽高赋值给canvas的高宽。

  • 整个数字距离左边边距的距离,占整个屏幕的4/5,leftDistance = Math.round(cw/10)

  • 整个数字距离上边距的距离,占整屏幕的1/5, topDistance = Math.round(ch/5)

  • 当绘制小时,分钟,秒钟时,就以左边距和上边距为起点,开始计算,此时就能直适应了。

绘制五角星的原理

star

大圆的半径为R,小圆的半径为r

注意此时的canvas的坐标系是Y轴向下,所以在计算Y轴的时候加上一个负号

此时M点的坐标为:

  • X轴:cos(18deg)*R
  • Y轴:-sin(18deg)*R

此时N点的坐标为:

  • X轴:cos(54deg)*R
  • Y轴:-sin(54deg)*R

后面的几个点依次加上72°,就能获得其他点的坐标了。

代码如下:

;(function(){
			var canvas = document.getElementById('pentagram');
			// 不支持的浏览器直接不显示
			if (!canvas || !canvas.getContext) {
				alert("您的浏览器太旧了,升级您的浏览器");
				return false;
			}
			var	context = canvas.getContext('2d')
				cw = document.documentElement.clientWidth || document.body.clientWidth,
				ch = document.documentElement.clientHeight || document.body.clientHeight;
				canvas.width = cw-50;
				canvas.height = ch-50;
				console.log(cw);
				console.log(ch);
			context.beginPath();
			context.lineWidth = 2;
			context.strokeStyle = 'red';
			for (var i = 0; i < 5; i++) {
				// 大圆起点
				context.lineTo(Math.cos((18 + i*72)/180*Math.PI)*300 + cw/2, -Math.sin((18 + i*72)/180*Math.PI)*300 + ch/2);
				// 小圆起点
				context.lineTo(Math.cos((54 + i*72)/180*Math.PI)*150 + cw/2, -Math.sin((54 + i*72)/180*Math.PI)*100 + ch/2);
			}
			context.closePath();
			context.stroke();
		})()

SVG学习笔记之十

textPath路径文本

<svg viewBox="0 0 1000 300"
     xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
  <!-- 文字路径 -->
    <path id="MyPath"
          d="M 100 200 
             C 200 100 300   0 400 100
             C 500 200 600 300 700 200
             C 800 100 900 100 900 100" />
  </defs>

  <use xlink:href="#MyPath" fill="none" stroke="red"  />

  <text font-family="Verdana" font-size="42.5">
  <!-- 文字利用的路径 xlink:href-->
    <textPath xlink:href="#MyPath">
      We go up, then we go down, then up again
    </textPath>
  </text>

  <!-- Show outline of the viewport using 'rect' element -->
  <rect x="1" y="1" width="998" height="298" fill="none" stroke="black" stroke-width="2" />
</svg>

结果如下图所示

textpath

text-anchor属性

这个属性text-anchor是设置文本水平排列效果。 有3个取值:

  • start
  • middle
  • end
<?xml version="1.0"?>
<svg width="120" height="120" viewBox="0 0 120 120"
     xmlns="http://www.w3.org/2000/svg" version="1.1">

    <!-- Materialisation of anchors -->
    <path d="M60,15 L60,110 M30,40 L90,40 M30,75 L90,75 M30,110 L90,110" stroke="grey" />
    
    
    <!-- Anchors in action -->
    <text text-anchor="start"
          x="60" y="40">A</text>

    <text text-anchor="middle"
          x="60" y="75">A</text>

    <text text-anchor="end"
          x="60" y="110">A</text>

    <!-- Materialisation of anchors -->
    <circle cx="60" cy="40" r="3" fill="red" />
    <circle cx="60" cy="75" r="3" fill="red" />
    <circle cx="60" cy="110" r="3" fill="red" />

<style><![CDATA[
text{
    font: bold 36px Verdana, Helvetica, Arial, sans-serif;
}
]]></style>
</svg>

text-anchor

dominant-baseline

dominant-baseline属性可设置文本垂直排列。

查看更多属性

路径文本的属性

文本在路径上的偏移可以用定位属性x,y,dx,dy,text-anchor和 startOffset属性

  • x/dx/startOffset可以设置字符在路径上的渲染起点。
  • y属性设置是没有任何效果的。
  • dy可设置字符在法线上的偏移。
  • text-anchor是设置文本水平排列效果。

textpath02

startOffset

对于路径文本的位置相对于路径的开始偏移量。取值为长度和百分比。
长度的话,startOffset表示在当前用户坐标系测量路径的距离
百分比的话,startOffset表示的是沿整个路径的百分比距离。因此,“0%”表示startOffset为起点的“路径”和“100%”表示startOffset =终点的“路径”。
该属性取负值无效。如果没指定该属性的话,那默认赋值为0。

注意:

  • 超出Path的部分,浏览器不进行渲染。

a----超链接

  • 可以添加到任意的图形上
  • xlink:href指定连接地址
  • xlink:title指定连接提示
  • target指定打开目标
<svg viewBox="0 0 1000 300"
     xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <a xlink:href="https://github.com/cy0707" xlink:title="ann0707">
    <rect x="100" y="100" width="100" height="100" fill="rgba(255, 0, 0, .5)"></rect>
  </a>
</svg>

参考文章

textPath
路径文本

SVG学习笔记之八

path概述

<path> 元素用于定义一个路径。

<path d="M0,0L10,20C30-40,40,20,100,100" stroke="red">

其中L10,20中的L代表的是命令,而10,20则代表的是参数。参数之间可以用空格或逗号隔开,有一种情况例外,就是下一个数值是负数的话,就不需要空格或者逗号隔开,负号可以直接充当分隔符。

例如看以下path的写法:

<path d="M0,0L10,20C30-10,40,20,100,100" stroke="red" />
<path d="M0 0 L 10 20 C 30 -10 40 20 100 100" stroke="red" />
<path d="M 0 0, L 10 20, C 30 -10 40 20 100 100" stroke="red" />

path命令

命令 含义
M/m(x, y)+ 移动到当前位置
L/l(x,y)+ 从当前位置绘制线段到指定位置
H/h(x,y)+ 从当前位置绘制水平线到达指定的x坐标
V/v(x,y)+ 从当前位置绘制竖直线到达指定的y坐标
Z/z 闭合当前路径
C/c(x1,y1,x2,y2,x,y)+ 从当前位置绘制三次贝塞尔曲线到指定位置
S/s(x2,y2,x,y)+ 从当前位置光滑绘制三次贝塞尔曲线到指定位置
Q/q(x1,y1,x,y)+ 从当前位置绘制二次贝塞尔曲线到指定的位置
T/t(x,y)+ 从当前位置光滑绘制二次贝塞尔曲线到指定位置
A/a(rx, ry, xr, laf, sf, x,y) 从当前位置绘制弧线到指定位置

M = moveto L = lineto H = horizontal lineto V = vertical lineto C = curveto
S = smooth curveto Q = quadratic Belzier curve T = smooth quadratic Belzier curveto
A = elliptical Arc Z = closepath

  • 区分大小写:大写表示坐标参数为绝对位置,小写则为相对位置
  • 最后的参数表示最终要到达的位置
  • 上一个命令结束的位置就是下一个命令开始的位置
  • 命令可以重复参数表示重复执行同一条命名

移动和直线命令

  • M(x, y)+ 移动画笔,后面如果有重复参数的话,会当做是L命令处理
  • L(x,y)+ 绘制直线到指定位置
  • H(x,y)+ 绘制水平线到指定的X位置
  • V(x,y)+ 绘制竖直线到指定的Y位置
  • m、l、h、v使用相对位置绘制

画了一个奇怪的形状,利用大写字母形成绝对坐标的例子。

<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M 10 10 L 300 300 H 400 400 V 100 100 Z" stroke="red" stroke-width="10" fill="blue"/>
</svg>

画了直角三角形,利用小写字母形成相对坐标的例子。

<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M 100 100 h 300 v 100 Z" stroke="red" stroke-width="10" fill="blue"/>
</svg>

弧线命令

  • A(rx, ry, xr, laf, sf, x, y)-----绘制弧线
  • rx----(radius-x)弧线所在椭圆的x半轴长
  • ry----(radius-y)弧长所在的椭圆的y半轴长
  • xr----(xAxis-rotation)弧线所在椭圆的长轴角度
  • laf----(large-arc-flag)是否选择弧长较长的那一段弧
  • sf----(sweep-flag)是否选择顺时针方向的那一段弧
  • x,y---弧的终点位置

当我们把一个起点好终点连接起来的距离不大于xr的两倍,则可以证明存在这样一个椭圆。如下图所示
椭圆都经过起点和终点。

a2

上图所画的的椭圆实际上是存在两个的,可以看出连接起点和终点的弧线一共有四条。这四条分别用两个参数来控制分别为laf和sf。

a1

画了一段圆弧的例子

<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M 200 200 h 100 l -100 100 v -100 M 300 200 A 100 100 0 0 1 200 300" stroke="red" fill="#ccc"/>
</svg>

贝塞尔曲线

二次赛贝尔曲线

sai01

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
</svg>

三次贝塞尔曲线

sai02

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>
  <path d="M70 10 C 70 20, 120 20, 120 10" stroke="black" fill="transparent"/>
</svg>

贝塞尔曲线----光滑曲线

  • T:Q的光滑版本
    • T是上一段曲线的控制点关于当前曲线起始点的镜像位置
  • S: C的简化版本
    • S是上一段曲线的控制点2关于当前曲线起始点的镜像位置

二次贝塞尔曲线:

语法:T x y (or t dx dy)

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 80 Q 52.5 10, 95 80 T 180 80" stroke="black" fill="transparent"/>
</svg>

shortcut_quadratic_bezier_with_grid

三次贝塞尔曲线

语法:S x2 y2, x y (or s dx2 dy2, dx dy)

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="black" fill="transparent"/>
</svg>

shortcut_cubic_bezier_with_grid

参考文章

贝塞尔曲线
Paths

SVG学习笔记之五

transform属性值

tranform属性用来对一个元素声明一个或多个变换。它输入一个带有顺序的变换定义列表的<transform-list>值。每个变换定义由空格或逗号隔开。给元素添加变换看起来如下:

有效地SVG变换有:旋转, 缩放, 移动, 和倾斜。transform属性中使用的变换函数类似于CSS中transform属性使用的CSS变换函数,除了参数不同。

Translation

要移动SVG元素,你可以用translate()函数。translate函数的语法如下:

translate(<tx> [<ty>])

translate()函数输入一个或两个值得来声明水平和竖直移动值。tx代表x轴上的translation值;ty代表y轴上的translation值。

ty值是可选的,如果省略,默认值为0。tx和ty值可以通过空格或者逗号分隔,它们在函数中不代表任何单位-它们默认等于当前用户坐标系单位。

下面的例子把一个元素向右移动100个用户单位,向下移动300个用户单位。

<circle cx="0" cy="0" r="100" transform="translate(100 300)" />

上述代码如果以translate(100, 300)用逗号来分隔值的形式声明一样有效。

scale

你可以通过使用scale()函数变换来向上或者向下缩放来改变SVG元素的尺寸。scale变换的语法是:

scale(<sx> [<sy>])

scale()函数输入一个或两个值来声明水平和竖直缩放值。sx代表沿x轴的缩放值,用来水平延长或者拉伸元素;sy代表沿y轴缩放值,用来垂直延长或者缩放元素。

sy值是可选的,如果省略默认值等于sx。sx和sy可以用空格或者逗号分隔,它们是无单位值。

下面例子把一个元素的尺寸根据最初的尺寸放大两倍:

<rect width="150" height="100" transform="scale(2)" x="0" y="0" />

这里需要注意当SVG元素缩放时,整个坐标系被缩放,导致元素在视窗中重新定位

Skew

SVG元素也可以被倾斜,要倾斜一个元素,你可以使用一个或多个倾斜函数skewX 和 skewY。

skewX(<skew-angle>) skewY(<skew-angle>)

函数skewX声明一个沿x轴的倾斜;函数skewY声明一个沿y轴的倾斜。

倾斜角度声明是无单位角度的默认是度

注意倾斜一个元素可能会导致元素在视窗中重新定位。

rotate

你可以使用rotate()函数来旋转SVG元素。这个函数的语法如下:

rotate(<rotate-angle> [<cx> <cy>])

rotate()函数对于给定的点和 旋转角度值执行旋转。不像CSS3中的旋转变换,不能声明除degress之外的单位。角度值默认无单位,默认单位是度

可选的cx和cy值代表无单位的旋转中心点。如果没有设置cx和cy,旋转点是当前用户坐标系的原点

SVG中默认的旋转中心是当前使用的用户坐标系的左上角,这样也许你无法创建想要的旋转效果,你可以在rotate()中声明一个新的中心点。如果你知道元素在SVG画布中的尺寸和定位,你可以把它的中心设置为旋转中心。

下面的例子是以当前用户坐标系中的(50,50)点为中心进行旋转一组元素:

<g id="parrot" transform="rotate(45 50 50)" x="0" y="0">
    <!-- elements making up a parrot shape -->
</g>

如果你想要一个元素围绕它的中心旋转,你也许想要像CSS中一样声明中心为50% 50%;不幸的是,在rotate()函数中这样做是不允许的-你必须用绝对坐标。

坐标系变化

transform属性被定义成两个在被添加的元素上建立新用户空间(当前坐标系)之一,viewBox属性是创建新用户空间的两个属性中的另一个。

当你在一个SVG元素上添加transform属性,元素获取当前使用的用户坐标系的一个“副本”。你可以当做给发生变换的元素创建一个新“层”,新层上是当前用户坐标系的副本(the viewBox)。

然后,元素新的当前坐标系被在transform属性中声明的变换函数改变,因此导致元素自身的变换。这看起来好像是元素在变换后的坐标系中重新绘制。

我们说变换添加在坐标系上,因此,元素最终被影响并且发生变换。那么究竟如何改变旋转中心工作在坐标系的原点(0,0)的点呢?

当你改变中心或者旋转时,坐标系被变换或者旋转特定角度,然后再次根据声明的旋转中心产生特定变换。在这个例子中:

<g id="parrot" transform="rotate(45 150 170)">

被浏览器当成一系列的移动和旋转等同于:

<g id="parrot" transform="translate(150 170) rotate(45) translate(-150 -170)">

嵌套和组合变换

很多时候你可能想要在一个元素上添加多个变换。添加多个变换意味着“组合”变换。

例如,如果你要在一个元素上添加旋转,接下来移动,移动变换会根据新的坐标系统,而不是初始的没有旋转时的系统

所以,效果上来说,嵌套变化类似于组合:唯一区别是不像在一个元素上添加一系列的变化,它自动从父元素上获得变换,最后执行添加在它自身的变换,就像我们在上面添加的变换一样-一个接一个。

参考文章

理解SVG坐标系统和变换: transform属性

HTML5 DOM的File API

简介

使用HTML5 DOM新增的File API,现在可以让网页要求用户选择本地文件,并且读取这些文件的信息了。选择的方式既可以是HTML<input> 元素,也可以是拖拽 。

<input type="file" id="input" multiple="multiple">
//multiple支持多个文件同时上传

通过File API,我们可以在用户选取一个或者多个文件之后,访问到代表了所选文件的一个或多个File对象,这些对象被包含在一个FileList对象中

如果用户只选择了一个文件,那么我们只需要访问这个FileList对象中的第一个元素.可以使用传统的DOM选择方法来获取到用户所选择的文件:

var selected_file = document.getElementById('input').files[0];

在 change 事件发生时读取所选择的文件

<input type="file" id="input" onchange="handleFiles(this.files)" multiple >

当用户成功选取若干个文件后,handleFiles()函数会被调用,且一个代表用户所选择的文件的包含了File 对象的FileList对象会作为参数传入该函数。this.files代表用户所上传的文件的集合,即fileList对象,
这个对象是是一个集合,其中每一个文件就是文件对象file

如果你的程序可以让用户选择多个文件,记得要在input元素上加上multiple属性:在用户选择了多个文件的情况下,传入handleFiles()函数的文件列表将会包含多个File对象,每个File对象对应一个真实的文件。

动态添加change事件监听器

你还可以通过element.addEventListener()方法来添加多个change事件处理函数,像这样:

var inputElement = document.getElementById("inputField");
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
//获取文件列表的集合
  var fileList = this.files; 
}

获取所选文件的信息

用户所选择的文件都存储在了一个FileList对象上,其中每个文件都对应了一个File对象。你可以通过这个FileList对象的length属性知道用户一共选择了多少个文件:

var numFiles = files.length;
//可以通过普通的循环语句来操作每个单独的File对象:
for (var i = 0, numFiles = files.length; i < numFiles; i++) {
  var file = files[i];
  ..
}

File对象上有三个属性提供了所包含文件的相关信息.

  • name:文件名,只读字符串,不包含任何路径信息.
  • size:文件大小,单位为字节,只读的64位整数.
  • type:MIME类型,只读字符串,如果类型未知,则返回空字符串.

在隐藏的文件输入框上调用click()方法

从Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)开始,你可以隐藏掉默认的的文件输入框<input>元素,使用自定义的界面来充当打开文件选择对话框的按钮。实现起来很简单,你只需要使用样式display:none把原本的文件输入框隐藏掉,然后在需要的时候调用它的click()方法就行了

<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<a href="#" id="fileSelect">Select some files</a>
var fileSelect = document.getElementById("fileSelect"),
  fileElem = document.getElementById("fileElem");

fileSelect.addEventListener("click", function (e) {
  if (fileElem) {
    fileElem.click();
  }
  e.preventDefault(); // prevent navigation to "#"
}, false);

这样,你就能任意改变这个文件选择按钮的样式了。

通过拖放操作选择文件

你可以让用户将本地文件拖放到你的应用程序上.

首先要创建一个拖放操作的目的区域。可根据您的应用程序的设计来决定哪部分的内容接受 drop,但创建一个接收drop事件的元素是简单的:

var dropbox;
//ID 为 dropbox 的元素所在的区域是我们的拖放目的区域。
//我们需要在该元素上绑定 dragenter,dragover,和drop 事件。
dropbox = document.getElementById("dropbox");
dropbox.addEventListener("dragenter", dragenter, false);
dropbox.addEventListener("dragover", dragover, false);
dropbox.addEventListener("drop", drop, false);

//我们必须阻止dragenter和dragover事件的默认行为,这样才能触发 drop 事件:
function dragenter(e) {
  e.stopPropagation();
  e.preventDefault();
}

function dragover(e) {
  e.stopPropagation();
  e.preventDefault();
}
//下面是 drop 函数:
function drop(e) {
  e.stopPropagation();
  e.preventDefault();

  var dt = e.dataTransfer;
  var files = dt.files;

  handleFiles(files);
}

在handleFiles(files)该函数中,我们从事件对象中获取到dataTransfer对象,把该对象包含的Filelist对象传入函数handleFiles(),这个函数会无区别的对待从input元素或拖放操作中来的文件列表。

显示用户所选图片的缩略图

function handleFiles(files) {
  for (var i = 0; i < files.length; i++) {
    var file = files[i];
    var imageType = /^image\//;
    
    if ( !imageType.test(file.type) ) {
// 当前这个文件不符合这个图片格式跳出本次循环
      continue;
    }
    
    var img = document.createElement("img");
    img.classList.add("obj");
    img.file = file;
    // 假设 "preview" 是将要展示图片的 div
    preview.appendChild(img);
    
    var reader = new FileReader();
    reader.onload = (function(aImg) { 
      return function(e) { 
        aImg.src = e.target.result; 
      }; 
    })(img);
    reader.readAsDataURL(file);
  }
}

这里我们循环处理用户选择的文件,查看每个文件的类型属性,看看这是否是一个图像文件(通过一个正则表达式匹配字符串“image.*”)。对于每个图片文件,我们创建一个新的img元素。CSS可以用于建立任何漂亮的边界,阴影,和指定图像的大小,所以,甚至不需要在这里完成。

每张图片我们添加一个obj类,让他们更容易的在DOM树中被找到。我们也在图片上添加了一个file属性来确认每张图片的 File,这样可以帮助我们在之后真正的上传工作时获取到图片。最后我们使用 Node.appendChild() 把缩略图添加到我们先前的文档区域中。

然后,我们建立了FileReader来处理图片的异步加载,并把它添加到img元素上。在创建新的FileReader对象之后,我们建立了onload函数,然后调用readAsDataURL()开始在后台进行读取操作。当图像文件的所有内容加载后,他们转换成一个 data: URL,传递到onload回调函数中。之后只需要把img元素的src属性设置为这个加载过的图像,就可以让图像的缩略图出现在用户的屏幕上。

使用对象URL---这个在javascript中issue中有这个例子

FileReader类型

FileReader类型实现的是一种异步文件读取机制。可以把FileReader想象成XMLHttpRequest.区别只是它读取的是文件系统,而不是远程服务器,为了读取文件中的数据,FileReader提供了如下几个方法

想要创建一个FileReader对象,很简单,如下:

var reader = new FileReader();

方法概述

  • abort();
  • readAsArrayBuffer(in Blob blob);
  • readAsBinaryString(in Blob blob);
  • readAsDataURL(in Blob blob);
  • readAsText(in Blob blob, [optional] in DOMString encoding);

参数:blob 将要读取的Blob对象或者File对象.
encodin( 可选 ) 一个字符串,表示了返回数据所使用的编码.如果不指定,默认为UTF-8.

abort()方法

中止该读取操作.在返回时,readyState属性的值为DONE.

readAsArrayBuffer()

开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个ArrayBuffer对象以表示所读取文件的内容.

readAsDataURL()

开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.

readAsBinaryString()

开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含所读取文件的原始二进制数据.

readAsText()

开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个字符串以表示所读取的文件内容.

FileReader的事件

  • onabort 当读取操作被中止时调用.
  • onerror 当读取操作发生错误时调用.
  • onload 当读取操作成功完成时调用.
  • onloadend 当读取操作完成时调用,不管是成功还是失败.该处理程序在onload或者onerror之后调用.
  • onloadstart 当读取操作将要开始之前调用.
  • onprogress 在读取数据过程中周期性调用.

参考文章

在web应用中使用文件
FileReader

Canvas学习笔记之一

基本概念

HTML5新增的canvas元素是专门用来绘图的。canvas元素就相当于在页面上放置了一块‘画布’,可以在其中进行图形的描绘。

基本步骤

  • 在HTML中放置一个canvas元素,指定其ID,width,height。
<canvas id='draw' width='400px' height='300px'></canvas>
  • 取得canvas元素进行绘制图形。
    • document.getElementById等方法取得canvas对象。
    • 取得上下文,即需要使用canvas对象的getContent方法来获取图形上下文。并将参数设置为2d因为只支持2d。
    • 填充与绘制图形的边框。共有两中方式即填充(fill)与绘制边框(stroke)。填充是指填满图形的内部;绘制边框是指不填满图形的内部,只绘制图形的边框。
    • 设置绘图样式----fillStyle属性即填充的样式,strokeStyle---图形边框的样式。
    • 指定线宽----lineWidth
    • 指定颜色值
    • 绘制图形---填充矩形content.fillRect(x, y, width, height) 和绘制边框content.strokeRect(x, y, width, height)其中content是指的图形上下文对象。x是指矩形的起点的横坐标,y是指矩形起点的纵坐标。坐标原点为canvas画布的最左上角。width指的是矩形的长度,height是指矩形的高度。
function draw() {
var canvas = document.getElementById('draw');
if(canvas == null) {
    return false;
}
var content =canvas.getContext('2d');
content.fillStyle = '#eef';
content.fillRect(0, 0, 400, 300);
content.fillStyle = 'red';
content.strokeStyle = 'blue';
content.lineWidth = 1;
content.fillRect(50, 50, 100, 100);
content.strokeRect(50, 50, 100, 100);
}

绘制矩形与清除矩形区域

绘制矩形

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  //实践表明在不设施fillStyle下的默认fillStyle=black
   content.fillRect(0, 0, 100, 100);
    //实践表明在不设施strokeStyle下的默认strokeStyle=black
   content.strokeRect(120, 0, 100, 100);

   //设置纯色
   content.fillStyle = "red";
   content.strokeStyle = "blue";
   content.fillRect(0, 120, 100, 100);
   content.strokeRect(120, 120, 100, 100);

   //设置透明度实践证明透明度值>0,<1值越低,越透明,值>=1时为纯色,值<=0时为完全透明
  content.fillStyle = "rgba(255,0,0,0.2)";
   content.strokeStyle = "rgba(255,0,0,0.2)";
   content.fillRect(240,0 , 100, 100);
  content.strokeRect(240, 120, 100, 100);            
}
window.onload = function() {
  draw();
}

清除矩形

clearRect(x, y, width, height)该方法将擦除指定的矩形区域中的图形。使得矩形区域中的颜色全部变成透明。其中X是指矩形起点的横坐标,Y是指矩形起点的纵坐标。坐标原点为canvas画布的最左上角。width是指矩形的长度;height是指矩形的高度。

content.clearRect(0, 0, 200, 200);  

使用路径

要想绘制除矩形以外的其他图形,需要使用路径。其步骤。

  • 开始创建路径
  • 创建图形的路径
  • 路径创建完成后,关闭路径
  • 设置绘制样式,调用绘制方法,绘制路径。

总结为:首先使用路径来勾勒图形的轮廓,然后设置颜色,进行绘制。

相关知识点

  • 开始创建路径: 使用图形上下文对象的beginPath方法,该方法的定义如下。
    content.beginPath() 该方法不使用参数,调用该方法,开始路径的创建。在几次循环地创建路径的过程中,每次开始创建时都要调用beginPath函数。

  • 创建圆形路径: 创建圆形路径时,需要使用图形上下文对象的arc方法,该方法定义如下:
    content.arc(x, y, radius, startAngle, endAngle, anticlockwise)其中X,Y分别代表绘制圆形的起点横坐标和纵坐标。radius为圆形的半径,startAngle和endAngle分别为开始角度和结束角度。anticlockwise为是否为逆时针方向进行绘制。是一个布尔值。true为逆时针,false为顺时针方向进行绘制。该方法除了可以进行绘制圆形还能绘制弧形。

  • 关闭路径:content.closePath()。将路径关闭后,路径的创建工作就完成了。但是此时只是路径创建完成而言。还没有真正绘制任何图形。

  • 设定绘制样式,进行图形绘制。

   content.fillStyle = 'rgba(255, 0, 0, 0.25)';
   content.fill();

fill()方法是填充图形,stroke()方法是绘制图形边框。因为路径已经决定了图形的大小,所以就不需要在该方法中使用参数来指定图形的大小

这是一个圆的画法。

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');

      content.beginPath();
      content.arc(200, 150, 100, 0, Math.PI * 2, true);
     content.closePath();
     content.fillStyle = 'rgba(0,255,0,0.25)';
     content.fill();    
}
window.onload = function() {
  draw();
}

circle

如果没有关闭路径会怎样?

如果不关闭路径,那么已经创建的路径会永远保存着,下一次画的路径跟上次路径连在一块,会绘制出意想不到的图形。其实可以通过PS,AI等工具进行路径绘制,再填充上色。你就会明白是怎样回事了。

所以当我们进行绘制的时候,要仔细计算好路径是从哪里开始的,又是从哪里结束的。

每次画路径都在前后加context.beginPath() 和context.closePath()。

  • 系统默认在绘制第一个路径的开始点为beginPath

  • 如果画完前面的路径没有重新指定beginPath,那么画其他路径的时候会将前面最近指定的beginPath后的全部路径重新绘制

  • 每次调用context.fill()的时候会自动把当次绘制的路径的开始点和结束点相连,接着填充封闭的部分。

绘制线段的 moveTo和lineTo

  • moveTo方法的作用是将光标移动到指定坐标点,绘制直线的时候以这个坐标点为起点。其用法为moveTo(x,y)----x表示指定坐标点的横坐标,y表示指定坐标点的纵坐标。

  • lineTo()方法与moveTo方法中指定的直线起点与参数中指定的直线终点之间绘制一条直线。该方法定义如下。lineTo(x,y)-----x代表指定坐标点的横坐标,y表示指定坐标点的纵坐标。

使用该方法绘制完直线后,光标自动移动到lineTo方法的参数所指定的直线终点。

每次画线都从moveTo的点到lineTo的点, 如果没有moveTo那么第一次lineTo的效果和moveTo一样,
每次lineTo后如果没有moveTo,那么下次lineTo的开始点为前一次lineTo的结束点。

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
    content.beginPath();
    content.strokeStyle = 'red';
    content.fillStyle = 'blue';
    content.lineWidth = 10;
    content.moveTo(100, 100);
    content.lineTo(200, 100);
    content.lineTo(50, 50);
    content.stroke();
}
window.onload = function() {
  draw();
}

线条样式

  • lineCap:设置或返回线条的结束端点样式。
  • lineJoin:设置或返回两条线相交时,所创建的拐角类型。
  • lineWidth:设置或返回当前的线条宽度。
  • miterLimit:设置或返回最大斜接长度。

lineCap 属性设置或返回线条末端线帽的样式(注意:"round" 和 "square" 值会使线条略微变长)

取值分别为以下值:

  • butt:默认,向线条的每个末端添加平直的边缘。
  • round:向线条的每个末端添加圆形线帽。
  • square:向线条的每个末端添加正方形线帽。

lineJoin 属性设置或返回所创建边角的类型,当两条线交汇时的连接方式。

取值分别为以下值:

  • miter:默认。创建尖角。
  • round: 创建圆角
  • bevel:创建斜角。

miterLimit 属性设置或返回最大斜接长度。斜接长度指的是在两条线交汇处内角和外角之间的距离。

img_miterlimitfig

注意:只有当 lineJoin 属性为 "miter" 时,miterLimit 才有效。
边角的角度越小,斜接长度就会越大。
为了避免斜接长度过长,我们可以使用 miterLimit 属性。
如果斜接长度超过 miterLimit 的值,边角会以 lineJoin 的 "bevel" 类型来显示(Fig 3):

img_miterlimitbevelfig

用法:content.miterLimit=10(自己规定数字)

arcTo方法在画布上创建介于两个切线之间的弧/曲线

语法:context.fillRect(x1,y1,x2,y2,r);

  • 弧的起点的 x 坐标
  • 弧的起点的 y 坐标
  • 弧的终点的 x 坐标
  • 弧的终点的 y 坐标
  • 弧的半径

arcto

绘制贝塞尔曲线(贝济埃、bezier) context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)

  • cp1x和cp1y----第一个控制点的横坐标和纵坐标
  • cp2x和cp2y----第二个控制点的横坐标和纵坐标
  • x和y---终点横坐标和纵坐标

绘制二次样条曲线context.quadraticCurveTo(qcpx,qcpy,qx,qy)

  • qcpx和qcpy:二次样条曲线控制点x坐标和y坐标

  • qx和qy: 二次样条曲线终点y坐标

参考文章

玩转html5画图
《HTML5与CSS3权威指南》

HTML5拖拽笔记之二

拖拽事件

事件 描述
dragstart 事件主体是被拖放元素,在开始拖放被拖放元素时触发,即开始的拖放的那一瞬间。
drag 事件主体是被拖放元素,在正在拖放被拖放元素时触发,从拖放开始直到拖放进行目标元素中。
dragend 事件主体是被拖放元素,在整个拖放操作结束时触发,即进入新目标元素的那一瞬间。
dragenter 事件主体是目标元素,在被拖放元素进入某元素时触发,即新进入的目标元素。
dragover 事件主体是目标元素,在被拖放在某元素内移动时触发
dragleave 事件主体是目标元素,在被拖放元素移出目标元素是触发,离开的上一个目标元素。
drop 事件主体是目标元素,在被拖拽的元素在目标元素上同时鼠标放开触发,新进入的目标元素。

为了减少事件,你可以在事件 ondragenter 的时候 绑定方法 ,而 ondragleave 的时候,删除 方法。最好不要绑定在 dragover 上,它就像 mouseover ,在拖动的过程中不断触发,对于浏览器的负担就很大了,浏览器还有可能崩溃。

成为目标元素

把任何元素变成有效的放置目标,其方法是重写dragover事件的默认行为。例如:假如你有一个ID为‘droptarget’的div元素,可以用如下代码把它变成一个放置目标。

var droptarget = document.getElementById('droptarget');
droptarget,ondragover = function(e){
    e.preventDefault();
}

在ondragover中一定要执行preventDefault(),否则ondrop事件不会被触发。另外,如果是从其他应用软件或是文件中拖东西进来,尤其是图片的时候,默认的动作是显示这个图片或是相关信息,并不是真的执行drop。此时需要用document的ondragover事件把它直接干掉。

dataTransfer对象

只有简单的拖放而没有数据变化是没有什么用的。为了在拖放操作时实现数据交换HTML5定义了dataTransfer对象来传递拖拽的数据。

因为它是事件对象的属性,所以只能在拖放事件的事件处理程序中访问dataTransfer对象。在事件处理程序中,可以使用这个对象的属性和方法来 完善拖放功能。

<div id="draggable" draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">
           This div is draggable
</div>

getData()和setData()

ataTransfer对象有 getData()和setData()两个主要方法,操作dataTransfer中携带的数据。不难想象,getData()可以取得由setData()保存的值。setData()方法的第一个参数,也是getDAta()方法唯一的一个参数,表示保存的数据类型。

IE只定义了“text”和“URL”两种有效的数据类型,而HTML5则对此加以扩展,允许指定各种MIME类型。考虑到向后兼容,HTML5也支持“text”和“URL”,但这两种类型会被映射为“text/plain”和“text/uri-list”。如下所示:

  • text/html:文本文字格式
  • text/plain:HTML代码格式
  • text/xml:XML字符格式
  • text/url-list:URL格式列表

保存在dataTransfer对象中的数据只能在drop事件处理程序中读取。如果在ondrop处理程序中没有读到数据,那就是dataTransfer对象已经被销毁,数据也丢失了。

在拖动文本框中的文本时,浏览器会调用setData()方法,将拖动的文本以“text”格式保存在dataTransfer对象中。类似地,在拖放链接或图像时,会调用setData()方法并保存URL。然后,在这些元素被拖放到放置目标时,就可以通过getData()读到这些数据。

当然,作为开发人员,你也可以在dragstart事件处理程序中调用setData(),手工保存自己要传输的数据,以便将来使用。

将数据保存为文本和保存为URL是有区别的。如果将数据保存为文本格式,那么数据不会得到任何特殊处理。而如果将数据保存为URL,浏览器会将其当成网页中的链接。换句话说,如果你把它放置到另一个浏览器窗口中,浏览器就会打开该URL。

Firefox在其第5个版本之前不能正确地将“URL”和“text”映射为“text/uri-list”和“text/plain”。但是却能把“Text”映射为“text/plain”。为了更好地在跨浏览器的情况下从dataTransfer对象取得数据,最好在取得URL数据时检测两个值,而在取得文本数据时使用“text”。

var dataTransfer = event.dataTransfer;
//读取URL
var url = dataTransfer.getData("url") || dataTransfer.getData("text/uri-list");
//读取文本
var text = dataTransfer.getData("Text");

注意:一定要把短数据类型放在前面,因为IE 10及之前的版本仍然不支持扩展的MIME类型名,而它们在遇到无法识别的数据类型时,会抛出错误。

dropEffect属性

dropEffect属性只有搭配effectAllowed属性才有用。effectAllowed属性表示允许拖放元素的哪种dropEffect,effectAllowed属性可能的值如下。

  • uninitialized:没有该被拖动元素放置行为。
  • none:被拖动的元素不能有任何行为。
  • copy:只允许值为“copy”的dropEffect。
  • link:只允许值为“link”的dropEffect。
  • move:只允许值为“move”的dropEffect。
  • copyLink:允许值为“copy”和“link”的dropEffect。
  • copyMove:允许值为“copy”和”link”的dropEffect。
  • linkMove:允许职位“link”和”move”的dropEffect。
  • all:允许任意dropEffect。

必须在ondraggstart事件处理程序中设置effectAllowed属性。

假设你想允许用户把文本框中的文本拖放到一个

元素中。首先,必须将dropEffect和effectAllowed设置为”move”。但是,由于
元素的放置事件的默认行为是什么也不做,所以文本不可能自动移动。重写这个默认行为,就能从文本框中移走文本。然后你就可以自己编写代码将文本插入到
中,这样整个拖放操作就完成了。如果将dropEffect和effectAllowed的值设置为”copy”,那就不会自动移走文本框中的文本。

兼容性

支持draggable属性的浏览器有IE10+、Firefox 4+、Safari 5+和Chrome。Opera 11.5以及之前的版本都不支持HTML5的拖放功能。另外,为了让Firefox 支持可拖动属性,还必须在dataTransfer对象中保存了一些信息。

在firefox下拖动出现新窗口的解决办法

发现在firefox下拖放,会出现新的tab 页签,即使在ondragover、ondrop中使用了event.preventDefault也无济于事,在mozllia上MDN的例子拖动也会弹出新的tab 页签。

####解决方法

dataTransfer.setData("asdf","")把这个第一个参数不设置提供的那些类型,设置其它的就可以,再设置为提供的那些类型,就会弹出新窗口。

参考文章

HTML Drag and Drop API
HTML 5的革新—— drag && drop(拖动)
html drag api 在firefox 下 拖动出现新窗口的解决办法

存储之cookie

概念

HTTP Cookie通常直接叫做cookie,最初是在客户端中用于存储会话信息,该标准要求服务器对任意的HTTP请求发送Set-Cookie HTTP头部作为响应的一部分,其中包含会话信息。例如,这个服务器响应头可能如下:

HTTP/1.1 200 OK
Content-type: text/html
Set-cookie:name=value
Other-header:other-header-value

这个HTTP响应设置以name为名称,以value为值的一个cookie,名称和值在发送的都必须是以URL编码,浏览器会存储这样的会话信息,并在这之后,通过为每个请求添加cookie HTTP头部信息发送会服务器,如下所示。

GET/index.html HTTP/1.1
Cookie: name=value
Other-header: other-header-value

发送回服务器的额外信息可以用于唯一验证客户来自于那个发送的那个请求。

cookie的构成

cookie由浏览器保存的以下几块信息构成

Sset-cookie: name=value; domain=.mozilla.org; expires=Feb, 13-Mar-2018 11:47:50; path=/; secure`

  • 名称:一个唯一确定cookie的名称,部分大小写,cookie的名字必须是经过URL编码的,必须使用decodeURIComponent()来解码。一般可以采用某个前缀在加上当前时间的做法,这样的话名称能够确保是唯一的,也比较方便。

  • 值:存储在cookie中的字符串值,必须经过被URL编码。

  • 域:对于哪个域是有效的,如果没有设置的话,默认来自设置cookie的那个域,在上诉例子中就是.Mozilla.org。

  • 失效时间:表示cookie何时应该被删除的时间戳,这个日期是GMT格式的日期,如果设置是以前的时间,cookie会被立刻删除。上诉cookie的失效时间是Feb,13-Mar-2018 11:47:50。

  • 路径:指定域中的那个路径,应该想服务器发送cookie,/ 表示没有限制

  • 安全标志:指定以后,cookie只有在使用SSL连接的时候才可以发送到服务器。

cookie的常见操作

var CookieUtil = {

//设置cookie
  set: function(name, value, expires, path, domain, secure) {
      var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
      //判断时间
      if (expires instanceof Date) {
        // 转为GTM时间
        cookieText += "; expires=" + expires.toGMTString();
      }
      // 如果设置了路径
      if(path){
          cookieText +="; path=" + path;
      }
      // 如果设置域名
      if(domain){
          cookieText += "; domain=" + domain;
      }
      // 如果设置安全
      if(secure){
          cookieText += "; secure";
      }

      // 写入cookie
      document.cookie = cookieText;
  },

//读取cookie
  get: function(name) {
        // 传入参数name
        var cookieName=encodeURIComponent(name) + "=",
        // 在document.cookie利用indexOf寻找
            cookieStart=document.cookie.indexOf(cookieName),
            cookieValue=null;
        // 如果找到,没有找到话,返回值为-1;
        if(cookieStart>-1){
            // 找到;分号之前的name对应的value的值,开始的起点是cookieStart
            var cookieEnd=document.cookie.indexOf(";",cookieStart);
            // 则最后的cookieEnd就是分号的位置
            // 如果没有找到分号,那么这个就是最后的哪一个cookie
            if(cookieEnd==-1){
                cookieEnd=document.cookie.Length;
            }
            //例如:cookie为tz=Asia%2FShanghai
            // 则取得的cookie的值。
            cookieValue=decodeURIComponent(document.cookie.substring(cookieStart+ cookieName.length, cookieEnd));
        }
        return cookieValue;
  },
//删除cookie
//重新定义cookie,把时间调为过去,原先的cookie就会失效,
//value也被设置为空值,这样就可以删除一个cookie
  unset: function(name, path, domain, secure) {
//    // new Date(0)则把时间设置为1970年
    this.set(name, "", new Date(0), path, domain, secure);
  }
}

//设置
CookieUtil.set('name', 'ann', new Date('January 1, 2010'),  '/books/projs/', 'www.wrox.com');
//读取
console.log(CookieUtil.get('name')); //ann
//删除
CookieUtil.unset('name');

cookie的理解

cookie是存储在用户的计算机的一个cookie的文件夹下,不同的系统,对应不同的路径中。客户端和服务器都可以操作cookie。在chrome浏览器中,在开发者工具下,可以找到cookie。客户端可以设置cookie发送给服务器,服务器接收到后,可以做出相对应的应答。同样,服务器同样可以设置cookie,发送给客户端,客户端也可以根据接收到的cookie做出相应的反应。

cookie的应用

  • 购物车(网购)
  • 自动登录(登录账号时的自动登录)
  • 精准广告平常浏览网页时有时会推出商品刚好是你最近浏览过,买过的类似东西,这些是通过cookie记录的。
  • 记住登录状态

cookie的优点

  • 通过良好的编程,控制保存在cookie中的session对象的大小。
  • 通过加密和安全传输技术(SSL),减少cookie被破解的可能性。
  • 只在cookie中存放不敏感数据,即使被盗也不会有重大损失。
  • 控制cookie的生命期,使之不会永远有效。偷盗者很可能拿到一个过期的cookie。、
  • 不需要服务器资源,直接存储在本地。

cookie的一些限制

  • cookie是 存于用户硬盘的一个文件,这个文件通常对应于一个域名,当浏览器再次访问这个域名时,便使这个cookie可用。因此,cookie可以跨越一个域名下的 多个网页,但不能跨越多个域名使用。

  • cookie对于每个浏览器有不同的大小和个数的限制。

    • IE6以及更低版本限制每个域名最多20个cookie
    • IE7之后的版本每个域名最多50个。
    • Firefox限制每个与最多50个cookie
    • Safari和Chrome对于每个域的cookie数量限制没有硬性规定。
  • cookie可能被禁用。当用户非常注重个人隐私保护时,他很可能禁用浏览器的cookie功能;

  • cookie是与浏览器相关的。这意味着即使访问的是同一个页面,不同浏览器之间所保存的cookie也是不能互相访问的;

  • cookie可能被删除。因为每个cookie都是硬盘上的一个文件,因此很有可能被用户删除;

  • cookie安全性不够高。所有的cookie都是以纯文本的形式记录于文件中,因此如果要保存用户名密码等信息时,最好事先经过加密处理。

参考文章

《javascript高级程序设计》

HTML5 Geolocation(地理定位)

概述

地理位置(Geolocation)是 HTML5 的重要特性之一,提供了确定用户位置的功能,借助这个特性能够开发基于位置信息的应用。

在访问位置信息前,浏览器都会询问用户是否共享其位置信息,以 Chrome 浏览器为例,如果您允许 Chrome 浏览器与网站共享您的位置,Chrome 浏览器会向 Google 位置服务发送本地网络信息,估计您所在的位置。然后,浏览器会与请求使用您位置的网站共享您的位置。鉴于该特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可用的。

浏览器的支持

HTML5 Geolocation API 用于获得用户的地理位置
Internet Explorer 9、Firefox、Chrome、Safari 以及 Opera 支持地理定位。
注释:对于拥有 GPS 的设备,比如 iPhone,地理定位更加精确。

HTML5 Geolocation API

if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(locationSuccess, locationError,{
        // 指示浏览器获取高精度的位置,默认为false
        enableHighAccuracy: true,
        // 指定获取地理位置的超时时间,默认不限时,单位为毫秒
        timeout: 5000,
        // 最长有效期,在重复获取地理位置时,此参数指定多久再次获取位置。
        maximumAge: 3000
    });
}else{
    alert("Your browser does not support Geolocation!");
}

locationError为获取位置信息失败的回调函数,可以根据错误类型提示信息

locationError: function(error){
    switch(error.code) {
        case error.TIMEOUT:
            showError("A timeout occured! Please try again!");
            break;
        case error.POSITION_UNAVAILABLE:
            showError('We can\'t detect your location. Sorry!');
            break;
        case error.PERMISSION_DENIED:
            showError('Please allow geolocation access for this to work.');
            break;
        case error.UNKNOWN_ERROR:
            showError('An unknown error occured!');
            break;
    }
}

locationSuccess为获取位置信息成功的回调函数,返回的数据中包含经纬度等信息,结合Google Map API 即可在地图中显示当前用户的位置信息,如下:

locationSuccess: function(position){
    var coords = position.coords;    
    var latlng = new google.maps.LatLng(
        // 维度
        coords.latitude,
        // 经度
        coords.longitude
    );  
    var myOptions = {  
        // 地图放大倍数  
        zoom: 12,  
        // 地图中心设为指定坐标点  
        center: latlng,  
        // 地图类型  
        mapTypeId: google.maps.MapTypeId.ROADMAP  
    };  
    // 创建地图并输出到页面  
    var myMap = new google.maps.Map(  
        document.getElementById("map"),myOptions  
    );  
    // 创建标记  
    var marker = new google.maps.Marker({  
        // 标注指定的经纬度坐标点  
        position: latlng,  
        // 指定用于标注的地图  
        map: myMap
    });
    //创建标注窗口  
    var infowindow = new google.maps.InfoWindow({  
        content:"您在这里<br/>纬度:"+  
            coords.latitude+  
            "<br/>经度:"+coords.longitude  
    });  
    //打开标注窗口  
    infowindow.open(myMap,marker); 
}

getCurrentPosition() 方法 - 返回数据

若成功,则 getCurrentPosition() 方法返回对象。始终会返回 latitude、longitude 以及 accuracy 属性。如果可用,则会返回其他下面的属性。

属性 | 描述
coords.latitude | 十进制数的纬度
coords.longitude | 十进制数的经度
coords.accuracy | 位置精度
coords.altitude | 海拔,海平面以上以米计
coords.altitudeAccuracy | 位置的海拔精度
coords.heading | 方向,从正北开始以度计
coords.speed | 速度,以米/每秒计
timestamp | 响应的日期/时间

总的来说,在PC的浏览器中 HTML5 的地理位置功能获取的位置精度不够高,如果借助这个 HTML5 特性做一个城市天气预报是绰绰有余,但如果是做一个地图应用,那误差还是太大了。不过,如果是移动设备上的 HTML5 应用,可以通过设置 enableHighAcuracy 参数为 true,调用设备的 GPS 定位来获取高精度的地理位置信息。

Geolocation 对象 - 其他有趣的方法

watchPosition() - 返回用户的当前位置,并继续返回用户移动时的更新位置(就像汽车上的 GPS)。
clearWatch() - 停止 watchPosition() 方法

一个例子

<p id="demo">点击这个按钮,获得您的坐标:</p>
<button onclick="getLocation()">试一下</button>
<script>
var x=document.getElementById("demo");
function getLocation()
  {
  if (navigator.geolocation)
    {
    navigator.geolocation.watchPosition(showPosition);
    }
  else{x.innerHTML="Geolocation is not supported by this browser.";}
  }
function showPosition(position)
  {
  x.innerHTML="Latitude: " + position.coords.latitude + 
  "<br />Longitude: " + position.coords.longitude;	
  }
</script>

参考文章

HTML5 地理位置定位(HTML5 Geolocation)原理及应用
HTML5 地理定位

HTML5存储

服务端的存储

在服务端的存储大致分为4个方面。

  1. cache
  2. 磁盘
  3. 数据库
  4. 内存

客户端的cookie存储(H5之前)

HTTP Cookie,通常称为cookie,最初是在客户端用于存储临时会话信息。该标准要求服务器对任意的HTTP请求发送Set-Cookie HTTP头作为相应的一部分,其中包含会话信息。

例如,这种服务器响应的头可能如下:

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name = value
Other-header: other-header-value

这个HTTP响应设置一name为名称,以value为值的一个cookie,名称和值在传送时都必须是URL编码的,浏览器会存储这样的会话信息,并在这之后,通过为每一个请求添加Cookie HTTP头将信息发送回服务器。

GET/ index.html HTTP/1.1
Cookie: name=value
Other-header: other-header-value

发送回服务器的额外信息可以用于验证客户来自于发送的那个请求。

cookie的限制

  1. cookie是绑定在特定的域名下,当设定一个cookie后,载创建它的域名发送请求时,会包含这个cookie,这个限制确保了存储在cookie中的信息只能让批准的接受者访问,而无法被其他域访问。
  2. 由于cookie是存在客户端计算机上面,还加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间。每个域的cookie总数是有限的,不过各个浏览器各有不同。每个域名最多不超过50或者30,20等等的cookie数量。
  3. 当超过单个域名限制之后还要设置cookie,浏览器就会清除以前设置的cookie。
  4. 浏览器对于cookie的尺寸也有限制,大多数浏览器都有大约409B(加1)的长度限制。尺寸限制影响到一个域下所有的cookie,而非每一个cookie单独限制。

cookie的的构成

cookie由以下几块信息构成

  1. 名称:一个唯一确定cookie的名称。cookie名称是不区分大小写的,然而实践中最好将cookie名称看作是区分大小写的,因为某些服务器会这样处理cookie,cookie的名称必须经过URL编码的。
  2. 值:存储在cookie中的字符串值,值必须经过URL编码。
  3. 域:cookie对于那个域有效。
  4. 路径:对于指定域中的那个路径。
  5. 失效时间: 表示cookie何时应该被删除的时间戳。
  6. 安全标志:指定后,cookie只能使用SSL连接的时候才发送到服务器。(例如:cookie信息只能发送给https://www.wrot.com,而http://www.wrot.com的请求则不能发送cookie)

UserData存储

1.只有IE支持。
2. 解析为XML文件
所以这个用的不多。

HTML5的几种存储

  • 本地存储(localstorage && sessionstorage)
  • 离线缓存(application cache)
  • IndexedDB 和 Web SQL

本地存储和离线存储的区别

html5的离线缓存是把你想要缓存的文件比如html css js文件通过离线缓存机制下载到电脑上 然后在没网的时候直接访问电脑上的文件。

本地存储是把网页上的某些数据按照键值对的方式存放在浏览器里面 一个域最多可以存5M数据 不分管离线还是在线 这些被存的数据都会一直在浏览器里面 除非手动清除数据。

HTML5的本地存储

本地存储可以存储数组,json数据,图片,脚本,css等等。
使用场景:利用本地数据,减少网络传输,在弱网络环境下,高延迟,低宽带,尽量把数据本地化。

  1. 两个localstorage与sessionstorage对象
  2. 存储形式 key--value
  3. 每个域名有5m
  4. 这个类型只能存储字符串,非字符串的数据会在存储之前被转换成字符串。

使用的注意事项:

  1. 使用前要判断浏览器是否支持。
  2. 写数据时候,需要异常处理,避免超出容量出错。
  3. 避免把敏感信息写入。
  4. key的唯一性。

localStorage对象API介绍

  1. clear():删除所有值
  2. getItem(name):根据指定的名字name获取对应的值。
  3. removeItem(name):删除由name指定的名值对儿。
  4. setItem(name, value):为指定的name设置一个对应的值。
  5. key(index):获得index位置处的值的名字。

(以上api同样时适用于sessionStorage对象)
6. 永久存储,永不失效,除非手动删除

localStorage.setItem("demokey2", "http://https://github.com/cy0707");
//通过key来获取value
var xx = localStorage.getItem("demokey2");
console.log(xx);
//清空所有的key-value数据。
//localStorage.clear();
console.log(localStorage.length);

使用localStorage对象,页面必须来自同一个域名(子域名无效),使用同一种协议,在同一个端口。

sessionStorage对象

这个对象存储特定的会话的数据,也就是该数据只保持到浏览器关闭。浏览器关闭之后就会消失。存储在sessionStorage中的数据可以跨越浏览器刷新而存在,同时如果浏览器支持的话,浏览器崩溃并重启之后依然可用。

 //添加key-value 数据到 sessionStorage
 sessionStorage.setItem("demokey1", "http://https://github.com/cy0707");
 //通过key来获取value
var dt = sessionStorage.getItem("demokey1");
 console.log(dt);
 //清空所有的key-value数据。
//sessionStorage.clear();
  console.log(sessionStorage.length);
 //添加key-value 数据到 sessionStorage
// sessionStorage 当浏览器关闭或者标签页关闭后,那么就
// 会清楚数据

IndexedDB(用来代替Web SQL)

indexed Database API 是在浏览器中保存结构化数据的一种数据库。

indexedDB是按域名分配独立空间,一个独立域名下可创建多个数据库,每一个数据库可以创建多个对象存储空间,一个对象存储空间可以存储读个对象数据。

indexedDB设计的操作完全是异步进行的,因此大多数操作会以请求方式进行,但这些操作会在后期执行,然后如果成功则返回结果,如果失败则返回错误,差不多每一次indexedDB操作,都需要你注册onerror或者onsuccess事件处理程序,以确保适当处理结果。

indexedDB是按域名分配独立空间,一个独立域名可以创建多个数据库,每个数据库可以创建多个对象的存储空间,一个对象可以存储多个数据对象。

Web SQL(已经被标准移除)

操作Web SQL本地数据库的最基本的步骤是:

第一步:openDatabase方法:创建一个访问数据库的对象。
第二步:使用第一步创建的数据库访问对象来执行transaction方法,通过此方法可以设置一个开启事务成功的事件响应方法,在事件响应方法中可以执行SQL.
第三步:通过executeSql方法执行查询,当然查询可以是:CRUD。

(1)openDatabase方法:

//Demo:获取或者创建一个数据库,如果数据库不存在那么创建之
var dataBase = openDatabase("student", "1.0", "学生表", 1024 * 1024, function () { });

openDatabase方法打开一个已经存在的数据库,如果数据库不存在,它还可以创建数据库。几个参数意义分别是:

1,数据库名称。
2,数据库的版本号,目前来说传个1.0就可以了,当然可以不填;
3,对数据库的描述。
4,设置分配的数据库的大小(单位是kb)。
5,回调函数(可省略)。
初次调用时创建数据库,以后就是建立连接了。

(2)db.transaction方法可以设置一个回调函数,此函数可以接受一个参数就是我们开启的事务的对象。然后通过此对象可以进行执行Sql脚本,跟下面的步骤可以结合起来。

(3)通过executeSql方法执行查询。

ts.executeSql(sqlQuery,[value1,value2..],dataHandler,errorHandler)

参数说明:

  1. sqlQuery:需要具体执行的sql语句,可以是create、select、update、delete;
  2. value1,value2..]:sql语句中所有使用到的参数的数组,在executeSql方法中,将s>语句中所要使用的参数先用“?”代替,然后依次将这些参数组成数组放在第二个参数中
  3. ataHandler:执行成功是调用的回调函数,通过该函数可以获得查询结果集;
  4. errorHandler:执行失败时调用的回调函数;
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <script src="../js/jquery-3.1.1.min.js" type="text/javascript"></script>
</head>
    <body>
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="txtName" id="txtName" required/></td>
            </tr>
               <tr>
                <td>标题:</td>
                <td><input type="text" name="txtTitle" id="txtTitle" required/></td>
            </tr>
            <tr>
                <td>留言:</td>
                <td><input type="text" name="txtWords" id="txtWords" required/></td>
            </tr>
        </table>
        <input type="button" value="保存" id="btnSave"/>
        <hr/>
        <input type="button" value="展示所哟数据" onclick="showAllTheData();"/>
        <table id="tblData">
        </table>
        <script type="text/javascript">
            function initDatabase() {
                var db = getCurrentDb();//初始化数据库
                if(!db) {
                    alert("您的浏览器不支持HTML5本地数据库");
                    return;
                }
                db.transaction(function (trans) {//启动一个事务,并设置回调函数
                    //执行创建表的Sql脚本
                    trans.executeSql("create table if not exists Demo(uName text null,title text null,words text null)", [], function (trans, result) {
                    }, function (trans, message) {//消息的回调函数alert(message);});
                }, function (trans, result) {

                }, function (trans, message) {

                });
            });
        }

            $(function () {//页面加载完成后绑定页面按钮的点击事件
                initDatabase();
                $("#btnSave").click(function () {
                    var txtName = $("#txtName").val();
                    var txtTitle = $("#txtTitle").val();
                    var txtWords = $("#txtWords").val();
                    var db = getCurrentDb();
                    //执行sql脚本,插入数据
                    db.transaction(function (trans) {
                        trans.executeSql("insert into Demo(uName,title,words) values(?,?,?) ", [txtName, txtTitle, txtWords], function (ts, data) {
                        }, function (ts, message) {
                            alert(message);
                        });
                    });
                    showAllTheData();
                });
            });

            function getCurrentDb() {
                //打开数据库,或者直接连接数据库参数:数据库名称,版本,概述,大小
                //如果数据库不存在那么创建之
                var db = openDatabase("myDb", "1.0", "it's to save demo data!", 1024 * 1024); ;
                return db;
            }

            //显示所有数据库中的数据到页面上去
            function showAllTheData() {
                $("#tblData").empty();
                var db = getCurrentDb();
                db.transaction(function (trans) {
                    trans.executeSql("select * from Demo ", [], function (ts, data) {
                        if (data) {
                            for (var i = 0; i < data.rows.length; i++) {
                                appendDataToTable(data.rows.item(i));//获取某行数据的json对象
                            }
                        }
                    }, function (ts, message) {
                        alert(message);
                        var tst = message;
                    });
                });
            }

            function appendDataToTable(data) {//将数据展示到表格里面
                //uName,title,words
                var txtName = data.uName;
                var txtTitle = data.title;
                var words = data.words;
                var strHtml = "";
                strHtml += "<tr>";
                strHtml += "<td>"+txtName+"</td>";
                strHtml += "<td>" + txtTitle + "</td>";
                strHtml += "<td>" + words + "</td>";
                strHtml += "</tr>";
                $("#tblData").append(strHtml);
            }
        </script>
    </body>
</html>

参考博文,感谢分享

SVG学习笔记之三

SVG中的结构化、分组和引用元素

##使用元素分组

中的g代表group(分组)的意思。分组元素用于在逻辑上对相关的图形元素进行分组。从图形编辑器的角度,例如Adobe Illustrator,元素提供了类似于Group Object的功能。你也可以认为分组和图形编辑器中图层的概念是相似的,因为一个图层就是一组元素。

元素将其所有子内容分到一组。它通常有一个id属性,用来给分组命名。你给元素应用的样式也都会被应用于它所有的子元素。所以它很容易添加样式、动画、交互,甚至整个组的对象的动画。

这和SVG中使用元素分组的原理是相同的。用SVG画一只鸟

<svg width="1144.12px" height="400px" viewBox="0 0 572.06 200">
    <style>
        svg{background-color:white;}
        #wing{fill:#81CCAA;}
        #body{fill:#B8E4C2;}
        #pupil{fill:#1F2600;}
        #beak{fill:#F69C0D;}
        .eye-ball{fill:#F6FDC4;}
    </style>
    <g id="bird">
        <g id="body">
            <path d="M48.42,78.11c0-17.45,14.14-31.58,31.59-31.58s31.59,14.14,31.59,31.58c0,17.44-14.14,31.59-31.59,31.59
            S48.42,95.56,48.42,78.11"/>
            <path d="M109.19,69.88c0,0-8.5-27.33-42.51-18.53c-34.02,8.81-20.65,91.11,45.25,84.73
            c40.39-3.65,48.59-24.6,48.59-24.6S124.68,106.02,109.19,69.88"/>
            <path id="wing" d="M105.78,75.09c4.56,0,8.84,1.13,12.62,3.11c0,0,0.01-0.01,0.01-0.01l36.23,12.38c0,0-13.78,30.81-41.96,38.09
            c-1.51,0.39-2.82,0.59-3.99,0.62c-0.96,0.1-1.92,0.16-2.9,0.16c-15.01,0-27.17-12.17-27.17-27.17
            C78.61,87.26,90.78,75.09,105.78,75.09"/>
        </g>
        <g id="head">
            <path id="beak" d="M50.43,68.52c0,0-8.81,2.58-10.93,4.86l9.12,9.87C48.61,83.24,48.76,74.28,50.43,68.52"/>
            <path class="eye-ball" d="M60.53,71.68c0-6.33,5.13-11.46,11.46-11.46c6.33,0,11.46,5.13,11.46,11.46c0,6.33-5.13,11.46-11.46,11.46
                C65.66,83.14,60.53,78.01,60.53,71.68"/>
            <path id="pupil" d="M64.45,71.68c0-4.16,3.38-7.53,7.54-7.53c4.16,0,7.53,3.37,7.53,7.53c0,4.16-3.37,7.53-7.53,7.53
                C67.82,79.22,64.45,75.84,64.45,71.68"/>
            <path class="eye-ball" d="M72.39,74.39c0-2.73,2.22-4.95,4.95-4.95c2.73,0,4.95,2.21,4.95,4.95c0,2.74-2.22,4.95-4.95,4.95
                C74.6,79.34,72.39,77.13,72.39,74.39"/>
        </g>
    </g>
</svg>

如果你想要改变#body分组的填充颜色,它里边的所有元素都会变成你指定的颜色,非常方便。

分组元素非常好用,不仅是因为其组织和结构的特性。当你想要给由几块内容组成的SVG图像添加交互或动画的时候,非常好用。你可以把这些内容项放到一个组中,然后给它们定义移动、缩放或旋转的动画,这样它们相互之间的空间关系就可以被保持,也就是位置不会被打乱。

如果你想要对整只鸟进行缩放,让它变成现在尺寸的两倍,只需要一行CSS即可完成。

#bird {
transform: scale(2);
}

分组尤其使得交互变得非常方便。你可以将鼠标事件应用到整只鸟上,然后让它作为一整个组去回应事件,而不必给组中的每个元素分别去应用相同的交互或者变换。

元素有一个更重要的特性:它可以有自己的<title>和标签,使其更容易被屏幕阅读器解读,而且代码整体的可读性也更好。例如:

<g id="bird">
    <title>Bird</title>
    <desc>An image of a cute little green bird with an orange beak.</desc>
    <!-- ... -->
</g>

使用重用元素

元素可以让你重用现有的元素,给你一个类似于图形编辑器中复制粘贴的功能。它可以用于重用单个元素,也可以重用一组用定义的元素。

元素有x,y,height,width属性,它通过使用xlink:href属性引用其它内容。所以如果你已经定义了一个分组,并给它赋予了id,当你想要在其它地方使用它时,你只需要在xlink:href属性中给一个URI,然后指定x和y的位置,也就是该组图像显示的原点(0, 0)。

例如,当我们想要在我们的SVG画布上创建另一只鸟时,代码如下:

<use x="100" y="100" xlink:href="#bird" />

你可以在xlink:href属性中引用任何SVG元素,即使该元素是存在于外部文件中的。引用的元素和组不需要一定存在于同一个文件中。这对于组织以及缓存文件来说是非常棒的(例如,你可以单独给要用于重用的元素建一个文件)。例如,如果我们示例中的鸟是在一个单独的叫做animals.svg的文件中创建的,我们可以像这样引用它:

但是,在中引用外部SVG在大多数版本的IE中是不行的(至少要IE11)。

现在,你可能已经注意到,中的x和y属性指定了分组元素开始的位置,也就是元素左上角应该处的位置。移动元素意味着你从当前位置开始,将其移动到另一个位置。我指的是“应该定位到”,它会暗示元素根据use中的坐标系统在整个画布上定位元素,对吧?

但是事实证明,x和y坐标系其实是使用变换属性平移元素的简写。更具体地说,上面的等同于:

<use xlink:href="#bird" transform="translate(100, 100)" />

但是,你不能在副本中覆盖初始元素的样式(例如描边和填充)。这也就意味着如果你想要创建多只鸟或多个图标,你可能希望每个图标都是不同的颜色,这是不可能用元素完成的,除非初始元素是在元素中定义的,并且没有应用这些样式。

使用重用已存储元素

元素可以用来存储那些我们不想直接显示的内容。换句话说,元素就是用来定义元件,但是不直接渲染。这个隐藏的存储元件可以在后面被其它SVG元素应用及显示,这使得它非常适合用于绘制那些包含重用图像的图案。

所以,使用我们可以定义一个我们想要使用的元素。这个元素可以是任何内容,可以是我们前面看到的一只鸟,也可以是裁剪路径、蒙版或一个线性渐变。基本上,任何内容,只要是我们想要定义并保存,然后在后面再使用的,我们都可以在中定义,而且该元件可以保存为模板,或是作为一种工具,以便将来使用。模板仅在实例化的时候显示。

下面的示例定义了一个SVG渐变,然后把它作为一个简单的SVG矩形的填充颜色:

<svg>
    <defs>
        <linearGradient id="gradient">
            <stop offset="0%" style="stop-color: deepPink"></stop>
            <stop offset="100%" style="stop-color: #009966"></stop>
        </linearGradient>
    </defs>

    <rect stroke="#eee" stroke-width="5" fill="url(#gradient)"></rect>
</svg>

在元素中定义线性渐变,就是确保该渐变不会被渲染,除非它在哪个需要的地方被引用了。

在上一节中我们提到了元素的两个缺陷:

  • 新元素的位置相对于初始元素定位。
  • 初始元素的样式不能在新副本中被覆盖。

的确,还包括重用use元素会在画布上渲染这一点。

使用元素,所有这些缺陷都可以避免。不仅不会渲染初始元素,而且当你想要重用中的元素时,你为每个示例指定的定位都是相对于用户坐标系统的原点,而不是相对于初始元素的位置(也就是初始元素是一个模板,甚至都不需要在画布上渲染出来)。

在这个示例中,我们有一棵树,这棵树由一个树干和一组树叶组成。树叶组成了一个组,id为id="leaves",这个组又和树干组合成了一个更大的叫做tree的组。

<svg width="500.79px" height="200px" viewBox="0 0 500.79 200">
    <style type="text/css">
        #leaves{fill:#8CC63F;}
        #bark{fill:#A27729;}
    </style>
    <g id="tree">
        <path id="bark" d="M91.33,165.51c0,0,4.18-27.65,1.73-35.82l-18.55-25.03l3.01-2.74l17.45,19.87l1.91-37.6h4.44l1.83,24.53
        l15.26-16.35l3.27,4.36l-16.07,19.34c0,0-2.72,0-1.09,19.34c1.63,19.34,3,29.7,3,29.7L91.33,165.51z"/>
        <g id="leaves">
            <path class="leaf" d="M96.97,79.07c0,0-14.92,4.34-23.52-14.05c0,0,19.4-7.98,24.37,11.9c0,0-9.68-3.57-13.07-6.73
                C84.75,70.2,91.82,77.99,96.97,79.07z"/>
            <path class="leaf" d="M74.07,100.91c0,0-15.94-1.51-17.2-22.39c0,0,21.62-0.27,18.83,20.66c0,0-7.92-7.1-9.97-11.41
                C65.73,87.77,69.55,97.92,74.07,100.91z"/>
            <!-- ... -->
        </g>
    </g>
</svg>

如果我们用一个元素包裹#tree组,这棵树就不会在画布上渲染。

<svg width="500.79px" height="200px" viewBox="0 0 500.79 200">
    <style type="text/css">
        #leaves{fill:#8CC63F;}
        #bark{fill:#A27729;}
    </style>
    <defs>
        <g id="tree">
            <!-- ... -->
        </g>
    </defs>
</svg>

现在这棵树就相当于一个模板。我们可以通过元素来使用它,就像我们use其它元素一样。唯一的不同是x和y属性现在是相对于用户坐标系统定位的而不是相对于使用的元素

例如,如果我们想要创建三个树的副本,然后在SVG画布上显示它们。假设在这种情况下,用户坐标系统匹配视窗的宽度和高度,初始位置也和SVG视窗的左上角重合,我们会得到如下的代码和结果:

<use xlink:href="#tree" x="50" y="100" />
<use xlink:href="#tree" x="200" y="100" />
<use xlink:href="#tree" x="350" y="100" />

当你使用来重用元素,你可以给它应用不同的样式,给每棵树填充不同的颜色,只要这些样式没有在初始的树模板中定义。如果中的树已经使用了这些样式,这些样式同样没办法被新实例的样式覆盖。所以非常适合用于创建实例很少的模板,然后给副本应用其需要的样式。如果没有,只用是不可能完成的。

元素中的内容不是渲染树的一部分,就像defs是一个g元素,其display的值被设置为none。然而,defs的子内容总是在源代码树中写出,然后被其它元素引用;因此,defs元素或它的任何子内容的display属性的值都不能阻止这些元素被其它元素引用,即使设置为none。

使用对元素进行分组

<symbol>元素和`元素相似——它提供了一种对元素进行分组的方式。但是,它和分组元素有两个主要的不同:

<symbol>元素不会被渲染。在这种方式中实际上它类似于元素。只有在use时才显示。
<symbol>元素可以有自己的viewBox和preserveAspectRatio属性。也就是它可以适应视窗,然后以你想要的任何方式渲染,而不是都按照默认的样式。

<symbol>非常适用于定义可重复使用的元件(或符号)。它也可以作为<use>元素实例化的一个模板。而且有viewBox和preserveAspectRatio属性,它可以在引用<use>元素定义的矩形视窗中自适应缩放。注意symbol元素每次被use元素实例化时都可以重新定义新的视窗。

此项功能是非常棒的,因为它允许你定义独立于它们渲染的视窗的元素,因此,确保你引用的symbol总是以某种方式显示在视窗中。

参考文章

SVG中的结构化、分组和引用元素

怎么解决这个问题ctx.globalCompositeOperation='xor'啊?

<!DOCTYPE html>
<html>
<body>

<canvas id="myCanvas" width="300" height="400" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>

<script>

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.globalCompositeOperation="xor";

ctx.fillStyle="red";
ctx.beginPath();
ctx.moveTo(100,60);
ctx.lineTo(160,120);
ctx.lineTo(100,180);
ctx.lineTo(40,120);
ctx.closePath();
ctx.fill();


ctx.fillStyle="bule";
ctx.beginPath();
ctx.lineTo(160,120);
ctx.lineTo(100,180);
ctx.lineTo(40,120);
ctx.closePath();
ctx.fill();


</script>

</body>
</html>


qq20180915-0

浏览器兼容的处理总结

meta标签

http://www.cnblogs.com/moyingliang/p/5748043.html
https://github.com/joshbuchea/HEAD
这两篇文章有详细的介绍

<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">

这是个是IE8的专用标记,用来指定IE8浏览器去模拟某个特定版本的IE浏览器的渲染方式,以此来解决部分兼容问题,如果支持Google Chrome Frame:GCF,则使用GCF渲染;搜索如果系统安装IE8或以上版本,则使用最高版本IE渲染;

<meta name=“renderer” content=“webkit|ie-comp|ie-stand”>

360安全浏览器6.5版本开始在中添加上一行代码:content的取值为webkit,ie-comp,ie-stand之一,区分大小写,分别代表用极速模式,兼容模式,IE模式打开。因为360浏览器是双内核, 分webkit内核和IE内核;为了打开是chrome内核向下面这样写

<meta name=“renderer” content=“webkit”>

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

对于手机适配,禁止用户缩放。

<meta name="format-detection" content="telephone=no">
<!-- Disable automatic detection and formatting of possible phone numbers -->

<meta name="description" content="A description of the page">

对于网站的描述

Favicons

<!-- For IE 10 and below -->
<!-- No link, just place a file called favicon.ico in the root directory -->
<!-- For IE 11, Chrome, Firefox, Safari, Opera -->
<link rel="icon" href="path/to/favicon-16.png" sizes="16x16" type="image/png">
<link rel="icon" href="path/to/favicon-32.png" sizes="32x32" type="image/png">
<link rel="icon" href="path/to/favicon-48.png" sizes="48x48" type="image/png">
<link rel="icon" href="path/to/favicon-62.png" sizes="62x62" type="image/png">
<link rel="icon" href="path/to/favicon-192.png" sizes="192x192" type="image/png">
<!-- More info: https://bitsofco.de/all-about-favicons-and-touch-icons/ -->

Apple iOS

<!-- Smart App Banner -->
<meta name="apple-itunes-app" content="app-id=APP_ID,affiliate-data=AFFILIATE_ID,app-argument=SOME_TEXT">

<!-- Disable automatic detection and formatting of possible phone numbers -->
<meta name="format-detection" content="telephone=no">

<!-- Add to Home Screen -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="App Title">

<!-- Touch Icons -->
<link rel="apple-touch-icon" href="path/to/apple-touch-icon.png">

<!-- In most cases, one 180×180px touch icon in the head is enough -->
<!-- Utilize the different icon sizes if you would want unique icons -->
<!-- determined by device. -->
<link rel="apple-touch-icon" sizes="57x57" href="path/to/[email protected]">
<link rel="apple-touch-icon" sizes="72x72" href="path/to/[email protected]">
<link rel="apple-touch-icon" sizes="114x114" href="path/to/[email protected]">
<link rel="apple-touch-icon" sizes="144x144" href="path/to/[email protected]">

<!-- iOS app deep linking -->
<meta name="apple-itunes-app" content="app-id=APP-ID, app-argument=http/url-sample.com">
<link rel="alternate" href="ios-app://APP-ID/http/url-sample.com">

QQ Mobile Browser

<!-- Locks the screen into the specified orientation -->
<meta name="x5-orientation" content="landscape/portrait">
<!-- Display this page in fullscreen -->
<meta name="x5-fullscreen" content="true">
<!-- Page will be displayed in "application mode"(fullscreen,etc.) -->
<meta name="x5-page-mode" content="app">

UC Mobile Browser

<!-- Locks the screen into the specified orientation -->
<meta name="screen-orientation" content="landscape/portrait">
<!-- Display this page in fullscreen -->
<meta name="full-screen" content="yes">
<!-- UC browser will display images even if in "text mode" -->
<meta name="imagemode" content="force">
<!-- Page will be displayed in "application mode"(fullscreen,forbiding gesture, etc.) -->
<meta name="browsermode" content="application">
<!-- Disabled the UC browser's "night mode" in this page -->
<meta name="nightmode" content="disable">
<!-- Simplify the page to reduce data transfer -->
<meta name="layoutmode" content="fitscreen">
<!-- Disable the UC browser's feature of "scaling font up when there are many words in this page" -->
<meta name="wap-font-scale" content="no">

IE的条件注释

可以看一下这两篇文章
http://uxc.360.cn/archives/562.html
https://msdn.microsoft.com/en-us/library/ms537512.aspx

<!--[if lt IE 7 ]><html class="ie6" lang="zh-cn"><![endif]-->
<!--[if IE 7 ]><html class="ie7" lang="zh-cn"><![endif]-->
<!--[if IE 8 ]><html class="ie8" lang="zh-cn"><![endif]-->
<!--[if IE 9 ]><html class="ie9" lang="zh-cn"><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html class="modern" lang="zh-cmn-Hans"><!--<![endif]-->

通过class来进行层级选择,来兼容各个浏览器的css。

<!--[if (gte IE 10)|!(IE)]><!-->
    <script type="text/javascript" src="js/jquery-2.1.4.min.js"></script>
    <!--<![endif]-->
    <!--[if lte IE 9]>
        <script type="text/javascript" src="js/jquery-1.11.1.min.js"></script>
    <![endif]-->

对于不同版本的浏览器的加载不同版本的jquery,来适配各种浏览器。

Canvas学习笔记之四

绘制文字

在HTML5中,可以在Canvas画布中进行文字的绘制,同时也可以指定绘制文字的字体、大小、对齐方式、还可以进行文字的纹理填充等。

绘制文字时,可以使用fillText方法或strokeText方法

fillText方法用来填充方式绘制字符串,该方法的定义如下所示:

content.fillText(text, x, y, [maxWidth]);

  • 第一个参数text表示要绘制的文字
  • x和y表示绘制文字的起点的横坐标和纵坐标
  • maxWidth为可选参数,表示显示文字时的最大宽度,可以防止文字溢出

strokeText方法用轮廓方式绘制字符串,该方法的定义如下所示:

content.fillText(text, x, y, [maxWidth])

该参数与上一个方法的参数是类似的。

在使用Canvas API来进行文字的绘制之前,可以先对该对象的有关文字绘制的属性进行设置,他们是:

  • font属性: 设置文字字体
  • textAlign属性: 设置文字水平对齐方式,属性值可以为start、end、left、right、center。默认值为start
  • textBaseline属性:设置文字垂直对齐方式,属性值可以为top、hanging、middle、alphabetic、ideographic、bottom。默认为ideographic。
function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
 content.fillStyle = '#00f';
 content.strokeStyle ='red';
 content.font = 'italic 30px sans-serif';
 content.textBaseline = 'top';
 // 填充字符串
 content.fillText('hello', 0, 0);
 content.font = 'bold 30px sans-serif';
 // 轮廓字符串
 content.strokeText('hello', 0, 50);
}
window.onload = function() {
  draw();
}

测量文字的宽度的方法:content.measureText()

content.measureText(text)

该方法接受一个参数text,该参数为需要绘制的文字。该方法返回一个TextMetrics对象,TextMetrics对象的width属性表示使用当前指定的字体后text参数中指定的文字的总文字宽度。

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  content.font = 'italic 30px sans-serif';
  // 定义绘制文字
  var txt = '字符串的宽度';
  // 获取文字的宽度
  var tm1 = content.measureText(txt);
  // 绘制文字
  content.fillText(txt, 10, 30);
  content.fillText(tm1.width, tm1.width + 10, 30);
  // 改变字体
  content.font = 'bold 30px sans-serif';
  // 重新获取文字的宽度
  var tm2 = content.measureText(txt);
  // 重新绘制文字
  content.fillText(txt, 10, 70);
  content.fillText(tm2.width, tm2.width + 10, 70);
}
window.onload = function() {
  draw();
}

txt

保存和恢复状态

在介绍图像剪裁的时候,有一个遗留的问题,如果接下来要想继续绘制别的图像,但是要取消掉剪裁范围,该怎样做呢?需要使用到Canvas API中的save和restore两个方法。

这两个方法均不带任何参数,分别保存与恢复图形上下文的当前绘图状态。这里的绘画状态指前面所讲的坐标原点,变形时的变换矩形,以及图形上下文对象的当前属性值等很多内容。

在需要保存和恢复当前状态时,首先调用save方法将当前状态保存在栈中,在做完想做的工作后,再调用restore从栈中取出之前保存的图形上下文的状态进行恢复,通过这种方法,对之后绘制的图像取消剪裁区域。

  • 保存:context.save() --- 当前状态封存
  • 恢复:context.restore() --- 把当前状态取出
function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  content.fillStyle = 'red';
  content.save(); //保存当前context的状态
  content.fillStyle = 'black';
  content.fillRect(0, 0, 100, 100);
  content.restore(); //恢复到刚刚保存的状态
  content.fillRect(110, 0, 100, 100);
}
window.onload = function() {
  draw();
}

所以先出现黑色的矩形,再是红色的矩形。

save

保存文件

Canvas API保存文件的原理实际上是把当前的绘画状态输出到一个data URL地址所指向的数据中的过程,
所谓data URL是指目前大多数浏览器能够识别的一种base64位编码的URL。

Canvas API使用toDataURL方法把绘画状态输出到一个data URL中,然后重新装载。客户可直接把装载后的文件进行保存。

dataURL的格式:data: image/png;base64,euryqeurwer.....

canvas.toDataURLtype)

  • type表示要输出数据的MIME类型
function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  content.fillStyle = 'rgb(0, 0, 255)';
  content.fillRect(0, 0, canvas.width, canvas.height);
  content.fillStyle = 'rgb(255, 255, 0)';
  content.fillRect(10, 20, 50, 50);
//把新画出来的图形地址存储为base64
  window.location = canvas.toDataURL('image/jpeg');
}
window.onload = function() {
  draw();
}

简单动画的制作

var w, h, i, content;
function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
   content =canvas.getContext('2d');
   w = canvas.width;
   h = canvas.height;
   i = 0;
  setInterval(rotate, 1000);
}
function rotate() {
  if (i==400) {
    // 跳出循环
    return;
  }
  content.clearRect(0, 0, w, h);
  content.fillStyle = 'red';
  content.fillRect(i, 0, 20, 20);
  i+=20;
}
window.onload = function() {
  draw();
}

Canvas学习笔记之二

绘制线性渐变图形

var lg = content.createLinearGradient(xStart,yStart,xEnd,yEnd)

lg.addColorStop(offset, color)

  • xstart:渐变开始点x坐标
  • ystart:渐变开始点y坐标
  • xEnd:渐变结束点x坐标
  • yEnd:渐变结束点y坐标
  • offset:设定的颜色离渐变结束点的偏移量(0~1)
  • color:绘制时要使用的颜色

lineargradient

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  var linearGradient = content.createLinearGradient(0, 0, 0, 300);
  linearGradient.addColorStop(0, '#E0EAFC');
  linearGradient.addColorStop(0.4, '#CFDEF3');
//填充渐变颜色
  content.fillStyle = linearGradient;
//填充图形
  content.fillRect(0, 0, 400, 300);
}
window.onload = function() {
  draw();
}

径向渐变

var lg = content.createRadialGradient(xStart,yStart,radiusStart,xEnd,yEnd,radiusEnd)

lg.addColorStop(offset, color)

  • xStart:发散开始圆心x坐标
  • yStart:发散开始圆心y坐标
  • radiusStart:发散开始圆的半径
  • xEnd:发散结束圆心的x坐标
  • yEnd:发散结束圆心的y坐标
  • radiusEnd:发散结束圆的半径
  • offset:设定的颜色离渐变结束点的偏移量(0~1)
  • color:绘制时要使用的颜色

radialgradient

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  var linearGradient = content.createRadialGradient(200, 150, 0, 200,150, 100);
  linearGradient.addColorStop(0, '#E0EAFC');
  linearGradient.addColorStop(0.4, '#CFDEF3');
  content.fillStyle = linearGradient;
  content.beginPath();
  content.arc(200, 150, 100, 0, Math.PI*2, true);
  content.closePath();
  content.fill();
}
window.onload = function() {
  draw();
}

绘制变形图形

坐标变换

  • 平移: 使用图形上下文对象的translate方法移动坐标轴原点。

content.translate(x, y)-----x代表将坐标轴原点向右移动多少个单位,默认情况下为像素;y代表将坐标轴原点向下移动多少个单位。

  • 扩大:使用图形上下文对象的scale方法将图形放大。

content.scale(x, y)-----x代表是水平方向放大的倍数,y是垂直方向的放大的倍数。将图形缩小的时候,将这两个参数设置为0到1之间的小数就可以了。

  • 旋转:使用图形上下文对象的rotate方法将图形进行旋转。

content.rotate(angle)---angel是指旋转的角度。旋转中心店是坐标轴的原点。旋转是以顺时针方向进行的,要想逆时针旋转时,将angle设定为负数。

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  content.fillStyle = '#eef';
  content.fillRect(0, 0, 400, 300);
// 图形绘制,移动的是坐标原点
  content.translate(200, 50);
  content.fillStyle = 'rgba(255, 0, 0, 0.25)';
  for (var i = 0; i < 50; i++) {
    content.translate(25, 25);
    content.scale(0.95, 0.95);
    content.rotate(Math.PI/10);
    content.fillRect(0, 0, 100, 50);
  }
}
window.onload = function() {
  draw();
}

得到如下图形:

can

坐标变换与路径的结合使用

如果对矩形进行变形,使用坐标变换就足够了。但是对使用路径绘制出来的图形进行变换的时候,要考虑的事件就比较多了。因为使用了坐标变换之后,已经创建好的路径就不能用了。必须重新创建路径。重新创建好路径之后,坐标变换方法又失效了。

此时,必须先另写一个创建路径的函数,然后在坐标变换的同时调用该函数。

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
  content.fillStyle = '#eef';
  content.fillRect(0, 0, 400, 300);
// 图形绘制
  content.translate(200, 50);
  content.fillStyle = 'rgba(255, 0, 0, 0.25)';
  for (var i = 0; i < 50; i++) {
    content.translate(25, 25);
    content.scale(0.95, 0.95);
    content.rotate(Math.PI/10);
    // 画五角星。坐标变换同时调用函数
    create5Star(content);
    content.fill();
  }
}
function create5Star(content) {
  var n = 0,
      dx =100,
      dy = 0,
      s = 50;
  // 创建路径
  content.beginPath();
  content.fillStyle = 'rgba(255, 0, 0, .5)';
  var x = Math.sin(0);
  var y = Math.cos(0);
  var dig = Math.PI/5*4;
  for (var i = 0; i < 5; i++) {
    var x = Math.sin(i*dig);
    var y = Math.cos(i*dig);  
    content.lineTo(dx + x*s, dy + y*s);
  }
  content.closePath();
}
window.onload = function() {
  draw();
}

can2

矩形变换

content.transform(m11,m12,m21,m22,dx,dy)

所谓的矩阵变换其实是content内实现平移,缩放,旋转的一种机制,主要原理就是矩阵相乘。

  • context.translate(x,y) 等同于context.transform (1,0,0,1,x,y)或context.transform(0,1,1,0.x,y)
  • context.scale(x,y)等同于context.transform(x,0,0,y,0,0)或context.transform (0,y,x,0, 0,0);
  • context.rotate(θ)等同于context.transform(Math.cos(θMath.PI/180),Math.sin(θMath.PI/180), -Math.sin(θMath.PI/180),Math.cos(θMath.PI/180),0,0)或 context.transform(-Math.sin(θMath.PI/180),Math.cos(θMath.PI/180),Math.cos(θMath.PI/180),Math.sin(θMath.PI/180), 0,0)

图形组合

我们可以将一个人图形重叠绘制在另一个图形上面。但是图形中能够被看到的部分完全取决于图形的绘制顺序。有时候想将两个图形进行组合,并且自己决定以那种方式进行组合,这是。就需要使用图形上下文对象的globalCompositeOperation属性决定自己的组合方式。

content.globalCompositeOperation = type

type的值必须是下面几种字符串之一

  • source-over(默认值):新图形覆盖在原图形之上

  • destination-over: 在原有图形下绘制新图形

  • source-in:显示原有图形和新图形的交集,新图形在上,所以颜色为新图形的颜色

  • destination-in:显示原有图形和新图形的交集,原有图形在上,所以颜色为原有图形的颜色

  • source-out:只显示新图形非交集部分

  • destination-out:只显示原有图形非交集部分

  • source-atop:显示原有图形和交集部分和未被重叠覆盖的原有图形,新图形在上,所以交集部分的颜色为新图形的颜色。

  • destination-atop:只绘制原有图形中被新图形覆盖的部分,与新图形的其他部分。原有图形中的其他部分变成透明。不绘制新图形中与原图形相重叠的部分。

  • lighter:原有图形和新图形都显示,交集部分做颜色叠加

  • xor:只绘制新图形中与原图形不重叠的部分,重叠部分变成透明。

  • copy:只绘制新图形,原有图形中未与新图形重叠的部分变成透明。

这是参考文章里的。大家可以根据图形进行理解。

function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
   var oprtns = new Array(
    "source-over",
    "destination-over",
  "source-in",
  "destination-in",
  "source-out",
  "destination-out",
  "source-atop",
  "destination-atop",
  "lighter",
   "xor",         
  "copy"
   );
  var i = 0;//组合效果编号

  //结合setinterval动态显示组合
  var interal = setInterval(function () {
      if (i == 10) {
          i=0;
      }
      else {
          i++;
      }
      //蓝色矩形
      content.fillStyle = "blue";
      content.fillRect(10, 10, 60, 60);
      //设置组合方式 
      content.globalCompositeOperation = oprtns[i];
      //设置新图形(红色圆形)
    content.beginPath();
    content.fillStyle = "red";
      content.arc(60, 60, 30, 0, Math.PI * 2, false);
      content.fill();
 }, 1000);
              
}
window.onload = function() {
  draw();
}

globalAlpha( 设置或返回绘图的当前 alpha 或透明值)

  • globalAlpha 属性设置或返回绘图的当前透明值(alpha 或 transparency)。
  • globalAlpha 属性值必须是介于 0.0(完全透明) 与 1.0(不透明) 之间的数字。

例如:首先,绘制一个红色的矩形,然后将透明度 (globalAlpha) 设置为 0.5,然后再绘制一个蓝色和一个绿色的矩形:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillStyle="red";
ctx.fillRect(20,20,75,50);
// Turn transparency on
ctx.globalAlpha=0.2;
ctx.fillStyle="blue"; 
ctx.fillRect(50,50,75,50); 
ctx.fillStyle="green"; 
ctx.fillRect(80,80,75,50);

alpha

给图形绘制阴影

  • context.shadowOffsetX :阴影的横向位移量(默认值为0)
  • context.shadowOffsetY :阴影的纵向位移量(默认值为0)
  • context.shadowColor :阴影的颜色
  • context.shadowBlur :阴影的模糊范围(值越大越模糊)
function draw(){
  var canvas = document.getElementById('draw');
  if (canvas == null) {
    return false;
  }
  var content =canvas.getContext('2d');
    content.shadowOffsetX = 10;
    content.shadowOffsetY = 10;
    content.shadowColor = 'rgba(100,100,100,0.5)';
    content.shadowBlur = 1.5;
    content.fillStyle = 'rgba(255,0,0,0.5)';
    content.fillRect(100, 100, 200, 100);
              
}
window.onload = function() {
  draw();
}

HTML5中使用isPointInPath(x,y)方法

HTML5中使用isPointInPath(x,y)方法

canvas画布中绘制的各个图形并不是以独立的DOM节点出现的。对于整个canvas画布才是一个DOM节点,这样:我们只能在整个canvas画布上添加事件监听和事件处理机制(即所有事件只能发生在canvas这个标签上),对于canvas画布中各个图形的访问性是个问题。

canvas针对路径绘图提供的一个方法:isPointInPath(x,y),我们用这个方法来模拟canvas画布中图形上的事件机制。

定义和用法

  • context.isPointInPath(x,y);//其中x,y分别是给定点的横、纵坐标。
  • isPointInPath()方法返回true,如果给定的点的坐标位于路径之内的话(包括路径的边)。

一个例子

<!DOCTYPE html>
<html>
 <body>
  <canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
    Your browser does not support the HTML5 canvas tag.</canvas>
  <script>
   var c=document.getElementById("myCanvas");
   var ctx=c.getContext("2d");
   ctx.rect(20,20,150,100);
   if (ctx.isPointInPath(100,100))
   {
    ctx.stroke();
   };
</script></body>
</html>

上例中,如果被检测点(100,100)位于ctx.rect(20,20,150,100)的路径之中,则ctx.stroke();即将这条路径绘制出来,很显然这个点的坐标是位于ctx的当前路径包含的区域之中,所以最终ctx.stroke();会执行。

通过上面的例子,我们应该注意两个概念:in the current path(在当前路径之中)和(当前)路径。

首先强调isPointInPath(x,y)方法是针对路径的,比如canvas中的rect、arc方法都可以用,但是fillRect不可以用,因为它不是路径;而且仅对当前的路径有效,而且如果当前路径有多个子路径(currentPath可以有多个subPath:比如进行了rect()之后,再进行arc(),最后关闭路径,进行stroke,那么rect()和arc()所绘制的就是当前路径的两个子路径),只对第一个子路径有效。
in the current path:如下图所示,图中路径是由rect方法形成的,in the path 包括path边。

point

把交互事件的坐标点转换为canvas中的坐标点

cnavasget

参考文章和课程

HTML5中使用isPointInPath(x,y)方法模拟事件处理
使用Canvas交互和isPointInPath(x,y)方法

SVG学习笔记之十一

SVG的中的剪切属性

剪切是一个图形化操作,你可以部分或者完全隐藏一个元素。被剪切的元素可以是一个容器也可以是一个图像元素。元素的哪些部分显示或隐藏是由剪切的路径来决定的。

css中clipPath属性

<clipPath>元素包括很多个基本图形(<rect><circle>等),<path>元素,甚至是<text>元素。

如果在<clipPath>里面通过<text>指定文本,那么这个文本就会当成是一个剪切路径,不管文是否可见,文本外的区域都将被剪切掉。

注意,你可以使用任何文本做为剪切路径。这为实现很多效果开启了一扇大门。你可以使用动画图片(比如,gif),甚至是视频,然后选择你需要的文本进行剪切。这里是没有任何限制的。

下面的示例,就是使用文本做为剪切路径:

<svg height="0" width="0">
    <defs>
        <clipPath id="svgTextPath">
            <text x="0" y="300" textLength="800px" lengthAdjust="spacing" font-family="Vollkorn" font-size="230px" font-weight="700" font-style="italic"> Blossom </text>
        </clipPath>
    </defs>
</svg>

在SVG中引用image元素

<image>中引用的SVG图像,设置的width和height属性,将会用来设置SVG视窗的大小。如果你引用的是像素图像(我们这里的例子就是这样做的),图像将会自动缩放到指定的width和height。所以我们要确认好他们的长宽比例,避免图像扭曲。

<svg height="500" width="800">
    <image xlink:href="flowers.jpg" x="0" y="0" width="800" height="500"/>
    <defs>
        <clipPath id="theSVGPath">
            <rect x="0" y="0" stroke="#000000" stroke-miterlimit="10" width="108" height="500"/>
            <rect x="121.5" y="25.5" stroke="#000000" stroke-miterlimit="10" width="55" height="455"/>
            <rect x="192.5" y="9.5" stroke="#000000" stroke-miterlimit="10" width="60" height="484"/>
            <rect x="271.5" y="44.5" stroke="#000000" stroke-miterlimit="10" width="63" height="416"/>
            <rect x="349.5" y="25.5" stroke="#000000" stroke-miterlimit="10" width="208" height="447"/>
            <rect x="574.5" y="44.5" stroke="#000000" stroke-miterlimit="10" width="60" height="446"/>
            <rect x="644.5" y="9.5" stroke="#000000" stroke-miterlimit="10" width="68" height="471"/>
            <rect x="736.5" y="18.5" stroke="#000000" stroke-miterlimit="10" width="49" height="462"/>
        </clipPath>
    </defs>
</svg>

使用clip-path将定义好的运用到元素上:

svg {
    clip-path: url(#theSVGPath);
}

SVG中的剪切——<clipPath>元素

在SVG中使用元素来定义剪切元素的剪切路径。----类似于PS中的剪切蒙版。

<svg>
    <defs>
        <clipPath id="myClippingPath">
            <!-- ... -->
        </clipPath>
    </defs>
    <!-- the element you want to apply the clipPath to can be any SVG element -->
    <g id="my-graphic" clip-path="url(#myClippingPath)">
        <!-- ... -->
    </g>
</svg>

<clipPath>元素中的内容可以是描述性的(如<title><desc><metadata>)。也可以是图形(如:<circle>, <ellipse>, <line>, <path>, <polygon>, <polyline>, <rect>或者<text>)。一个<clipPath>可以包含一个<use>元素或者<script>

注意,在<clipPath>元素中使用<use>元素,只能引用一些简单的SVG的图形(前面提到的),例如,它在<clipPath>内不能用于群体的参照,它是没办法正常工作的。

<clipPath>可以包括一个使用<animate>, <animateColor>, <animateMotion>,<animateTransform><set>创建的动画。这为很多创造打开了一扇门,只要你敢想,就能做。

请注意,<clipPath>的内容也不能包括<g>。例如我们给多个圆<circle>放在一个组里<g>,那么它不能正常工作,剪切路径不会运用到图片上。

<clipPath>中的clipPathUnits属性

clipPathUnits主要用来给<clipPath>元素内容指定一个坐标系统。它具有两个值:objectBoundingBox和userSpaceOnUse,其中userSpaceOnUse是默认值。

clipPathUnits = "userSpaceOnUse | objectBoundingBox"

userSpaceOnUse 属性的时候,按照视窗的作为剪切路径的坐标系统。

当使用了objectBoundingBox属性之后,图像自身的边框就会做为剪切路径的坐标系统。

clip-path-10

有一点需要特别的注意:当你设置了objectBoundingBox值后,元素中的内容必须在指定的坐标[0,1]内。坐标系统将成为一个单元系统,剪切出来的形状都在这个clipPath分值内。

<mask>----类似于ps中的蒙版(黑的部分显示,不蒙住图片。白的部分不显示,即蒙住图片)

屏蔽是一种不透明度值的组合和裁剪。像裁剪,您可以使用图形,文字或路径定义掩码的部分。一个掩码的默认状态是完全透明的,也就是裁剪平面的对面的。在掩码的图形设置掩码的不透明部分。

看一个例子:

  <svg width="800" height="800" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
      <linearGradient id="Gradient">
        <stop offset="0" stop-color="white" stop-opacity="0" />
        <stop offset="1" stop-color="white" stop-opacity="1" />
      </linearGradient>
      <mask id="Mask">
        <rect x="240" y="0" width="200" height="200" fill="url(#Gradient)"  />
      </mask>
    </defs>

    <rect x="0" y="0" width="200" height="200" fill="green" />
    <rect x="240" y="0" width="200" height="200" fill="red" mask="url(#Mask)" />
  </svg>

透明度的属性

  • opacity属性可用在填充和描边上面
<svg width="200" height="200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="100" height="100" opacity=".5" />
  <rect x="0" y="0" width="200" height="200" fill="blue" />
  <circle cx="100" cy="100" r="50" stroke="yellow" stroke-width="40" stroke-opacity=".5" fill="red" />
</svg>

参考文章

CSS和SVG中的剪切——clip-path属性和元素
clipPath
Clipping and masking

SVG学习笔记之六

SVG线性渐变

<svg width="660" height="220">
  <defs>
    <linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%"   stop-color="#05a"/>
      <stop offset="100%" stop-color="#0a5"/>
    </linearGradient>
  </defs>

  <rect x="10" y="10" width="600" height="200" fill="url(#linear)" />
</svg>
  • <defs>标签内定义了一个线性渐变,并给它赋了一个id为linear。这个id用于在后面矩形引用它作为填充的时候使用。

  • <linearGradient>元素,以及它的四个属性。x和y的值确定渐变的起点和终点。在这里,x方向上渐变从0%开始,到100%结束;y方向上保留在0%的位置。也就是说这是一个水平渐变。x和y的值可以是百分比或者是用0.0到1.0之间的数表示。如果没有指定数值,那么就默认是0或者0%。

  • <linearGradient>标签内定义了两个颜色结点,使用元素定义。第一个颜色结点在offset为0%的位置设置了一个蓝色,第二个结点在100%的位置设置了绿色。中间就是渐变冲蓝色到绿色的渐变。

<svg width="600" height="600">
	<defs>
		<linearGradient id="grad1" x1="0" y1="0" x2="50%" y2="0">
			<stop offset="0" stop-color="red"/>
			<stop offset="0.5" stop-color="blue"/>
			<stop offset="1" stop-color="green"/>
		</linearGradient>	
	</defs>
	<rect x="100" y="100" fill="url(#grad1)" width="200" height="150"></rect>
</svg>

这个x1="0" y1="0" x2="50%" y2="0"表示前百分50%,是红色到蓝色到绿色的渐变,后50%是全是绿色,没有渐变。

渐变除了可以作为填充,也可以作为描边。这里我把前面实例的矩形稍作修改。填充设置为纯色,把渐变变成矩形的描边

<rect x="100" y="100" stroke="url(#grad1)" stroke-width="10" fill="yellow" width="200" height="150"></rect>

你可以看到渐变现在已经变成了描边,而不是填充。我把描边设置得足够宽,这样我们可以很清楚地看到从0%到50%位置的渐变,和前面的示例一样。

元素的属性

元素一共有属性,offset,stop-color,stop-opacity。前两个我们都已经看过了。

  • offset:对于线性渐变,offset表示渐变矢量的位置。它定义了渐变结点的位置。它可以是0到1之间的值,也可以是0%到100%之间的值。

  • stop-color:定义颜色的在offset结点位置的颜色

  • stop-opacity:定义颜色结点的透明度,它可以是0到1之间的值,或者是0%到100%之间的值。

<svg width="600" height="600">
	<defs>
		<linearGradient id="grad1" x1="0" y1="0" x2="50%" y2="0">
			<stop offset="0" stop-color="red" stop-opacity="0.2"/>
			<stop offset="0.5" stop-color="blue" stop-opacity="0.6"/>
			<stop offset="1" stop-color="green" stop-opacity="0.4"/>
		</linearGradient>	
	</defs>
	<rect x="100" y="100" fill="url(#grad1)"  fill="yellow" width="200" height="150"></rect>
</svg>

元素的属性

  • x1, x2, y1, y2:定义渐变的起点和终点。

  • id:用于渐变的引用

  • xlink:href:在一个渐变中引用另一个渐变的方法。被引用的渐变的属性是可继承的,也可以被重写。

  • gradientUnit:它有两个的值,userSpaceOnUse和objectBoundingBox,这用于决定渐变是否随着引用它的元素进行缩放。也就是说它决定了x1、y1、x2、y2的缩放。userSpaceOnUse:x1、y1、x2、y2表示当前用户坐标系统的坐标。也就是说渐变中的值都是绝对值。objectBoundingBox:x1, y1, x2, y2表示应用渐变的元素创建的边界坐标系统。也就是说渐变随着应用的元素进行了缩放。

  • gradientTransform属性:允许你给任何渐变添加像rotate()和transition()这样的变换

<!-- 链接上面的例子-->
<linearGradient id="gradient3" x1="0" y1="0" x2="0" y2="1" xlink:href="#gradient1" gradientTransform="rotate(-50)" />
<rect x="0" y="300" width="600" height="200" fill="url(#gradient3)" />

渐变颜色旋转了一定的角度。

  • spreadMethod属性值:pad、reflect和repeat。

    • pad:(默认值)使用渐变的颜色结点来填充剩余的空间。例如,如果第一个结点是20%,那么0%到20%这部分就是相同的颜色。

    • reflect: 映射渐变图案,从'start-to-end',再从'end-to-start',然后'start-to-end',直到空间都填满了。

    • repeat:重复渐变图案,从起点->终点,直到空间填满。

<svg width="660" height="220">
  <defs>
    <linearGradient id="linear" x1="40%" y1="0%" x2="60%" y2="0%" spreadMethod="pad">
      <stop offset="0%"   stop-color="#05a"/>
      <stop offset="100%" stop-color="#0a5"/>
    </linearGradient>
  </defs>

  <rect x="10" y="10" width="600" height="200" fill="url(#linear)" />
</svg>

svg01

xlink:href属性

<svg width="660" height="220">
     <linearGradient id="gradient1">
	    <stop id="stop1" offset="0%" stop-color="red" stop-opacity="0.5" />
	    <stop id="stop2" offset="100%" stop-color="blue"/>
     </linearGradient>
	<!--  xlink:href="#gradient1" 继承 gradient1 -->
     <linearGradient id="gradient2" x1="0" y1="0" x2="0" y2="1" xlink:href="#gradient1"/>
	<rect x="0" y="0" width="600" height="200" fill="url(#gradient2)" />
</svg>

canvas图形的总结

canvas的兼容

如果想在IE6/7/8版本中使用canvas的话,引入google提供的explorercanvas的js文件,这个能实现大部分的canvas接口。

其官网地址explorercanvas

canvas的图形库

离屏canvas

将第二个canvas中的内容加载到第一个canvas上。

//页面中的canvas标签
var canvas = document.getElementById('canavs');
	// 直接创建一个canvas标签,把其作为离屏的canvas标签
	var watermark = document.createElement('canvas');
	// 不支持的浏览器直接不显示
	if (!canvas || !canvas.getContext) {
		alert("您的浏览器太旧了,升级您的浏览器");
		return false;
	}
	var	context = canvas.getContext('2d'),
		watermarkContext = watermark.getContext('2d'),
		cw = 1152,
		ch = 768,
		watermarkCw = 400,
		watermarkCh = 100,
		canvas.width = cw;
		canvas.height = ch;	
//在页面的canvas中把离屏的canvas内容加载在这个canvas内容中
context.drawImage(watermark, cw/2-watermarkCw/2, ch/2-watermarkCh/2);

CanvasRenderingContext2D

在其原型上添加自己的方法,使用context来调用自己的方法。

//方法的定义functionName 为自己定义的方法名,注意不要跟原型内置的名字
//重合,避免一些错误
 CanvasRenderingContext2D.prototype.functionName = function(){
//具体的实现方法
}

//方法的调用
context.functionName();

indexedDB

indexedDB

indexed Database API 或者简称为IndexedDB,是在浏览器中保存的结构化数据的一种数据库。IndexedDB是为了代替目前已废弃的Web SQL Database API而出现的。

IndexedDB设计的操作完全是异步进行的,因此,多数操作会以请求方式进行,但这些操作会在后期执行,然后如果成功则返回结果,如果失败则返回错误。差不多每一次IndexedDB操作,都需要注册onerror和onsuccess事件处理程序,以确保适当地处理结果。

由于 IndexedDB 本身的规范还在持续演进中,当前的 IndexedDB 的实现还是使用浏览器前缀。在规范更加稳定之前,浏览器厂商对于标准 IndexedDB API 可能都会有不同的实现。但是一旦大家对规范达成共识的话,厂商就会不带前缀标记地进行实现。实际上一些实现已经移除了浏览器前缀:IE 10,Firefox 16 和 Chrome 24。当使用前缀的时候,基于 Gecko 内核的浏览器使用 moz 前缀,基于 WebKit 内核的浏览器会使用 webkit 前缀等等

// In the following line, you should include the prefixes of implementations you want to test.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
// DON'T use "var indexedDB = ..." if you're not in a function.
// Moreover, you may need references to some window.IDB* objects:
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)
[/pre]

if (!window.indexedDB) {
    window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.")
}

数据库

IndexedDB就是一个数据库,其最大的特色是使用对象保存数据,而不是使用表来保存数据。一个IndexedDB数据库,就是一组位于相同命名空间下的对象的集合。

  • 使用IndexedDB的第一步就是打开它,即把要打开的数据库名传给indexedDB.open()。如果传入的数据库已经存在,就会发送一个打开它的请求;如果传入的数据库还不存在,就会发送一个创建并打开它的请求。

  • indexDB.open()会返回一个IDBRequest对象,在这个对象上可以添加onerror和onsuccess事件处理程序

  • 该 open 方法接受第二个参数,就是数据库的版本号。这样我们就可以更新数据库的 schema ,也就是说如果我们打开的数据库不是我们期望的最新版本的话,我们可以对 object store 进行创建或是删除。

var request,
      database;
request = indexedDB.open('admin');
request.onerror = function(event){
    console.log('somthing bad happended while trying to open:' + event.target.errorCode);
}
request.onsuccess= function(event){
    database = event.target.result;
   console.log(database);
}

在上面两个事件处理程序中,event.target都指向request对象,因此他们可以互相换用。如果响应onsuccess事件处理程序,那么event.target.result中将有一个数据库实例对象(IDBData-base)。如果发生错误,那么event.target.errorCode中将保存一个错误码,表示问题的性质。参见一下

错误码 含义
IDBDatabaseException.UNKNOW_ERR(1) 意外错误,无法归类
IDBDatabaseException.NON_TRANSIENT_ERR(2) 操作不合法
IDBDatabaseException.NON_FOUND_ERR(3) 未发现要操作的数据库
IDBDatabaseException.CONSTRAINT_ERR(4) 违反了数据库约束
IDBDatabaseException.DATA_ERR(5) 提示给事务的数据不能满足要求
IDBDatabaseException.NOT_ALLOWED_ERR(6) 操作不合法
IDBDatabaseException.TRANSACTION_INACTIVE_ERR(7) 试图重用已完成的事务
IDBDatabaseException.ABORT_ERR(8) 请求中断,未成功
IDBDatabaseException.READ_ONLY_ERR(9) 试图在只读模式下写入或修改数据
IDBDatabaseException.TIMEOUT_ERR(10) 在有效时间内未完成操作
IDBDatabaseException.QUOTA_ERR(11) 磁盘空间不足

创建和更新数据库版本号

要更新数据库的 schema,也就是创建或者删除对象存储空间,需要实现 onupgradeneeded 处理程序,这个处理程序将会作为一个允许你处理对象存储空间的 versionchange 事务的一部分被调用。

// 该事件仅在较新的浏览器中被实现
request.onupgradeneeded = function(event) { 
   // 更新对象存储空间和索引 .... 
};

在数据库第一次被打开时或者当指定的版本号高于当前被持久化的数据库的版本号时,这个 versionchange 事务将被创建

版本号是一个unsigned long long数字,这意味着它可以是一个非常大的整数。

这也意味着你不能使用浮点数,否则它会被转换成最接近的较小的整数并且事务可能不会启动,同样 upgradeneeded 事件也不会作为一个结果返回。例如不要使用 2.4 作为版本:

var request = indexedDB.open("MyTestDatabase", 2.4); 
// don't do this, as the version will be rounded to 2

构建数据库

现在来构建数据库。IndexedDB 使用对象存储空间而不是表,并且一个单独的数据库可以包含任意数量的对象存储空间。每当一个值被存储进一个对象存储空间时,它会被和一个键相关联。键的提供可以有几种不同的方法,这取决于对象存储空间是使用 key path 还是 key generator。

下面的表格显示了几种不同的提供键的方法。

key Path key Generator 描述
No No 这种对象存储空间可以持有任意类型的值,甚至是像数字和字符串这种基本数据类型的值。每当我们想要增加一个新值的时候,必须提供一个单独的键参数。
Yes No 这种对象存储空间只能持有 JavaScript 对象。这些对象必须具有一个和 key path 同名的属性。
No Yes 这种对象存储空间可以持有任意类型的值。键会为我们自动生成,或者如果你想要使用一个特定键的话你可以提供一个单独的键参数。
Yes Yes 这种对象存储空间只能持有 JavaScript 对象。通常一个键被生成的同时,生成的键的值被存储在对象中的一个和 key path 同名的属性中。然而,如果这样的一个属性已经存在的话,这个属性的值被用作键而不会生成一个新的键。

你也可以使用对象存储空间持有的对象,不是基本数据类型,在任何对象存储空间上创建索引。索引可以让你使用被存储的对象的属性的值来查找存储在对象存储空间的值,而不是用对象的键来查找。

此外,索引具有对存储的数据执行简单限制的能力。通过在创建索引时设置 unique 标记,索引可以确保不会有两个具有同样索引 key path 值的对象被储存。因此,举例来说,如果你有一个用于持有一组 people 的对象存储空间,并且你想要确保不会有两个拥有同样 email 地址的 people,你可以使用一个带有 unique 标识的索引来确保这些。

// 我们的客户数据看起来像这样。
const customerData = [
  { ssn: "444-44-4444", name: "Bill", age: 35, email: "[email protected]" },
  { ssn: "555-55-5555", name: "Donna", age: 32, email: "[email protected]" }
];
const dbName = "the_name";

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // 错误处理程序在这里。
};
request.onupgradeneeded = function(event) {
  var db = event.target.result;

  // 创建一个对象存储空间来持有有关我们客户的信息。
  // 我们将使用 "ssn" 作为我们的 key path 因为它保证是唯一的。
  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

  // 创建一个索引来通过 name 搜索客户。
  // 可能会有重复的,因此我们不能使用 unique 索引。
  objectStore.createIndex("name", "name", { unique: false });

  // 创建一个索引来通过 email 搜索客户。
  // 我们希望确保不会有两个客户使用相同的 email 地址,因此我们使用一个 unique 索引。
  objectStore.createIndex("email", "email", { unique: true });

  // 在新创建的对象存储空间中保存值
  for (var i in customerData) {
    objectStore.add(customerData[i]);
  }
};

正如前面提到的,onupgradeneeded 是我们唯一可以修改数据库结构的地方。在这里面,我们可以创建和删除对象存储空间以及构建和删除索引。

对象存储空间仅调用 createObjectStore() 就可以创建。这个方法使用存储空间的名称,和一个对象参数。
即便这个参数对象是可选的,它还是非常重要的,因为它可以让你定义重要的可选属性和完善你希望创建的对象存储空间的类型。

在我们的示例中,我们请求了一个名为“customers” 的对象存储空间并且定义了一个 使得存储空间中每个单独的对象都是唯一的属性作为 key path。

在这个示例中的属性是 “ssn”,被确保是唯一的。被存储在对象存储空间中的所有对象都必须存在“ssn”。

我们也请求了一个名为 “name” 的着眼于存储的对象的 name 属性的索引。如同 createObjectStore(),createIndex() 使用了一个完善了我们希望创建的索引类型的可选的 options 对象。添加一个不带 name 属性的对象也会成功,但是这个对象不会出现在 “name” 索引中。

增加和删除数据

在你可以对新数据库做任何事情之前,你需要开始一个事务。事务来自于数据库对象,而且你必须指定你想让这个事务跨越哪些对象存储空间。另外,你需要决定你是否将要对数据库进行更改或者你只是需要从它里面进行读取。虽然事务具有三种模式(只读,读写,和版本变更),在可以的情况下你最好还是使用只读事务,因为它们可以并发运行。

var transaction = db.transaction(["customers"], "readwrite");
// 注意: 旧版实验性的实现使用不建议使用的常量 IDBTransaction.READ_WRITE 而不是 "readwrite"。
// 如果你想支持这样的实现,你只要这样写就可以了: 
// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE);

transaction()

transaction()接受三个参数,(其中两个是可选的)并且返回一个事务对象。

  • 第一个参数是事务希望跨越的对象存储空间的列表,如果你希望事务能够跨越所有的对象存储空间你可以传入一个空数组。
  • 如果你没有为第二个参数指定任何内容,你得到的是只读事务。因为这里我们是想要写入所以我们需要传入 "readwrite" 标识。
  • 事务可以接收三种不同类型的 DOM 事件: error,abort,以及 complete

现在有了一个事务了,你将需要从它拿到一个对象存储空间。事务只能让你拿到一个你在创建事务时已经指定过的对象存储空间。然后你可以增加所有你需要的数据。

// 当所有的数据都被增加到数据库时执行一些操作
transaction.oncomplete = function(event) {
  alert("All done!");
};

transaction.onerror = function(event) {
  // 不要忘记进行错误处理!
};

var objectStore = transaction.objectStore("customers");
for (var i in customerData) {
  var request = objectStore.add(customerData[i]);
  request.onsuccess = function(event) {
    // event.target.result == customerData[i].ssn
  };
}

产生自 add() 调用的请求的 result 是被添加的值的键。因此在这种情况下,它应该等于被添加的对象的 ssn 属性, 因为对象存储空间使用 ssn 属性作为 key path。

注意 add() 函数要求数据库中不能已经有相同键的对象存在。如果你正在试图修改一个现有条目,或者你并不关心是否有一个同样的条目已经存在,使用 put()函数。

从数据库中删除数据

var request = db.transaction(["customers"], "readwrite")
                .objectStore("customers")
                .delete("444-44-4444");
request.onsuccess = function(event) {
  // 删除数据成功!
};

从数据库中获取数据

现在数据库里已经有了一些信息,你可以通过几种方法对它进行提取。首先是简单的 get()。你需要提供键来提取值,像这样:

var transaction = db.transaction(["customers"]);
var objectStore = transaction.objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {
  // 错误处理!
};
request.onsuccess = function(event) {
  // 对 request.result 做些操作!
  alert("Name for SSN 444-44-4444 is " + request.result.name);
};

对于一个“简单”的提取这里的代码有点多了。下面看我们怎么把它再缩短一点,假设你在数据库的级别上来进行的错误处理:

db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
};

这是如何工作的呢?由于只有一个对象存储空间,你可以避免传入一个在你的事务中需要的对象存储空间的列表,而只是作为一个字符串把名字传入即可。

同样,你只是在从数据库读取数据,所以你不需要一个 "readwrite"事务。调用一个没有指定模式的 transaction()将给你一个 "readonly"事务。

这里的另外一个微妙之处在于你实际上不需要保存请求对象到一个变量。因为 DOM 事件把这个请求作为它的 target,你可以使用 event 来得到 result 属性。

使用游标

使用 get() 要求你知道你想要检索哪一个键。如果你想要遍历对象存储空间中的所有值,那么你可以使用游标。看起来会像下面这样:

var objectStore = db.transaction("customers").objectStore("customers");

objectStore.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
    cursor.continue();
  }
  else {
    alert("No more entries!");
  }
};

openCursor() 函数

openCursor() 函数需要几个参数如下:

  • 你可以使用一个 key range 对象来限制被检索的项目的范围。
  • 你可以指定你希望进行迭代的方向

在上面的示例中,我们在以升序迭代所有的对象。游标成功的回调有点特别。游标对象本身是请求的 result (上面我们使用的是简写形式,所以是 event.target.result)。

然后实际的 key 和 value 可以根据游标对象的 key 和 value 属性被找到。如果你想要保持继续前行,那么你必须调用游标上的 continue() 。

当你已经到达数据的末尾时(或者没有匹配 openCursor() 请求的条目)你仍然会得到一个成功回调,但是 result 属性是 undefined。

使用游标的一种常见模式是提取出在一个对象存储空间中的所有对象然后把它们添加到一个数组中,像这样:

var customers = [];

objectStore.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    customers.push(cursor.value);
    cursor.continue();
  }
  else {
    alert("Got all customers: " + customers);
  }
};

使用索引

使用 SSN 作为键来存储客户数据是合理的,因为 SSN 唯一地标识了一个个体(对隐私来说这是否是一个好的想法是另外一个话题,不在本文的讨论范围内)。如果你想要通过姓名来查找一个客户,那么,你将需要在数据库中迭代所有的 SSN 直到你找到正确的那个。以这种方式来查找将会非常的慢,相反你可以使用索引。

var index = objectStore.index("name");
index.get("Donna").onsuccess = function(event) {
  alert("Donna's SSN is " + event.target.result.ssn);
};

“name” 游标不是唯一的,因此 name 被设成 "Donna" 的记录可能不止一条。在这种情况下,你总是得到键值最小的那个。

如果你需要访问带有给定 name 的所有的记录你可以使用一个游标。你可以在索引上打开两个不同类型的游标。

  • 一个常规游标映射索引属性到对象存储空间中的对象。
  • 一个键索引映射索引属性到用来存储对象存储空间中的对象的键。不同之处被展示如下:
index.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.key 是一个 name, 就像 "Bill", 然后 cursor.value 是整个对象。
    alert("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
     cursor.continue();
  }
};

index.openKeyCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.key is 一个 name, 就像 "Bill", 然后 cursor.value 是那个 SSN。
    // 没有办法可以得到存储对象的其余部分。
    alert("Name: " + cursor.key + ", SSN: " + cursor.value);
    cursor.continue();
  }
};

指定游标的范围和方向

如果你想要限定你在游标中看到的值的范围,你可以使用一个 key range 对象然后把它作为第一个参数传给 openCursor() 或是 openKeyCursor()。

你可以构造一个只允许一个单一 key 的 key range,或者一个具有下限或上限,或者一个既有上限也有下限。边界可以是闭合的(也就是说 key range 包含给定的值)或者是“开放的”(也就是说 key range 不包括给定的值)。这里是它如何工作的:

// 只匹配 "Donna"
var singleKeyRange = IDBKeyRange.only("Donna");

// 匹配所有在 "Bill" 前面的, 包括 "Bill"
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");

// 匹配所有在 “Bill” 前面的, 但是不需要包括 "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);

// Match anything up to, but not including, "Donna"
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);

//Match anything between "Bill" and "Donna", but not including "Donna"
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);

index.openCursor(boundKeyRange).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Do something with the matches.
    cursor.continue();
  }
};

有时候你可能想要以倒序而不是正序(所有游标的默认顺序)来遍历。切换方向是通过传递 prev 到 openCursor() 方法来实现的

objectStore.openCursor(null, IDBCursor.prev).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Do something with the entries.
    cursor.continue();
  }
};

因为 “name” 索引不是唯一的,那就有可能存在具有相同 name 的多条记录。要注意的是这种情况不可能发生在对象存储空间上,因为键必须永远是唯一的。如果你想要在游标在索引迭代过程中过滤出重复的,你可以传递 nextunique (或 prevunique 如果你正在向后寻找)作为方向参数。 当 nextunique 或是 prevunique 被使用时,被返回的那个总是键最小的记录。

index.openKeyCursor(null, IDBCursor.nextunique).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Do something with the entries.
    cursor.continue();
  }
};

当一个 web app 在另一个标签页中被打开时的版本变更

当你的 web app 在这样一种方式下改变你的数据库时碰到被要求进行版本变化,你需要考虑如果用户已经在一个标签页中打开了你的应用的旧版本的数据库,然后他又在另一个标签页中加载了你的应用的新版本,这种情况下会发生什么事情。当你带着比数据库实际版本更高的版本号调用 open() 时,所有其他打开的数据库必须在你开始实际对数据库进行修改之前显式通知这个请求。这里是它如何工作的:

var openReq = mozIndexedDB.open("MyTestDatabase", 2);

openReq.onblocked = function(event) {
  // 如果其他标签页已经加载了这个数据库,那么
  // 在我们可以继续处理之前它需要被关闭。
  alert("Please close all other tabs with this site open!");
};

openReq.onupgradeneeded = function(event) {
  // 所有其它数据库都已经被关掉了。Set everything up.
  db.createObjectStore(/* ... */);
  useDatabase(db);
}  

openReq.onsuccess = function(event) {
  var db = event.target.result;
  useDatabase(db);
  return;
}

function useDatabase(db) {
  // 确保添加一个如果另一个页面请求一个版本变化时来被通知的处理程序。
  // 我们必须关闭这个数据库。这就允许其他页面对数据库进行升级。
  // 如果你不这么做的话,除非用户关闭标签页否则升级就不会发生。
  db.onversionchange = function(event) {
    db.close();
    alert("A new version of this page is ready. Please reload!");
  };

  // 其他针对数据库的处理
}

数据库的删除

indexedDB.deleteDatabase('数据库的名字');

补充知识

   var objectStore = thisDb.createObjectStore(db_store_name, {keyPath: 'id', autoIncrement: true});
// keyPath是自增加的,所以每次都会增加1,虽然前面 的数据被删除了,但是
//先创建的还是从删除的最后一个keyPath开始加1

indexedDB的API

IndexedDB

参考文章

《javascript高级程序设计》
使用 IndexedDB
HTML5的存储

requestAnimationFrame的总结

概述

window.requestAnimationFrame()这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数,以满足开发者操作动画的需求。这个方法接受一个函数为参数,该函数会在重绘前调用。

注意: 如果想得到连贯的逐帧动画,函数中必须重新调用 requestAnimationFrame()。

requestAnimationFrame不需要使用者指定循环间隔时间,浏览器会基于当前页面是否可见、CPU的负荷情况等来自行决定最佳的帧速率,从而更合理地使用CPU。

使用requestAnimationFrame有什么好处?

浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果。比如,通过requestAnimationFrame(),JS动画能够和CSS动画/变换或SVG SMIL动画同步发生。另外,如果在一个浏览器标签页里运行一个动画,当这个标签页不可见时,浏览器会暂停它,这会减少CPU,内存的压力,节省电池电量。

语法

requestID = window.requestAnimationFrame(callback);   // Firefox 23 / IE10 / Chrome / Safari 7 (incl. iOS)
requestID = window.mozRequestAnimationFrame(callback);    // Firefox < 23
requestID = window.webkitRequestAnimationFrame(callback); // Older versions Chrome/Webkit
requestID = window.msRequestAnimationFrame(callback); // Older versions IE
requestID = window.oRequestAnimationFrame(callback); // Older versionsOpera

兼容写法

(function() {
    var lastTime = 0;
    var vendors = ['webkit', 'moz'];
    for(var x = 0; x  vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame =
          window[vendors[x]+'CancelAnimationFrame']  || window[vendors[x]+'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); },
              timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };

    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}());

与setTimeout/setInterval的区别

fps(画面每秒传输帧数)对于很多显示器都是60,对应显示频率就是16.7ms/次。所以设置定时器时如果时间间隔低于这个数值,某些帧是无法绘制的,就会造成所谓“过渡绘制”了,可能还会影响应用的性能。而requestAnimationFrame跟着浏览器的绘制间隔走,不会过渡绘制,资源高效利用,自然流畅。

setTimeout/setInterval函数是是根据自己设定的时间来进行循环,性能会差一些。

一个例子

首先定义一个基础的requestAnimFrame动画方法,如下:

var animate =function(){
......
requestAnimationFrame(animate);
}

在上面代码中,我们使用requestAnimationFrame自调用animate方法,生成动画所需的循环调用。从而实现动画。

参考文章

window.requestAnimationFrame
requestAnimationFrame

前端面试总结

前言

突然想起以前面试的时候,面试官问我的问题,有好多我都回答不出来,所以专门面试问题记录下面。

     --------------------------------------  2016.8 ---------------------------------------

问题一:四大浏览器的内核的名字以及这些浏览器私有前缀?

  • IE浏览器的内核------ Trident 私有前缀 -ms
  • Firefox浏览器的内核 ------ Gecko 私有前缀 -moz
  • Operal浏览器的内核 ------ Presto 私有前缀 -o
  • 谷歌浏览器的内核 ------ webkit 私有前缀 -webkit

问题二: 浏览器标准模式和怪异模式之间的区别是什么?

由于历史的原因,不同浏览器对页面的渲染是不同的,甚至同一浏览器的不同版本也是不同的。在W3C标准出台之前,不同的浏览器在页面的渲染上没有同一的规范,产生了差异,即Quirks mode(怪异模式或兼容模式);当W3C标准出台之后,不同浏览器对页面的渲染有了统一的标准,即Strict mode(标准模式或严格模式);这就是两者之间的区别。

在标准模式中,浏览器根据规范呈现页面;在混杂模式中,页面以一种比较宽松的向后兼容的方式显示。

如果XHTML文档包含形式完整的DOCTYPE,那么它一般以标准模式呈现。

对于HTML 4.01文档,包含严格DTD的DOCTYPE页面以标准模式呈现。包含过渡DTD和URI的DOCTYPE也导致页面以标准模式呈现,但是有过渡DTD而没有URI会导致页面以混杂模式呈现。

DOCTYPE不存在或形式不正确会导致HTML和XHTML文档以混杂模式呈现。

     --------------------------------------  2017.2.24 ---------------------------------------

自我介绍

记录这次的面试情况吧,在面试的时候,面试官让我自我介绍,好吧,这么多次的面试,都是从自我介绍开始,那好吧那我就自我介绍,自我介绍的时候,我说的是,我先后工作的两个公司的情况,说我如果如何由一个UI设计师成为一个前端切图仔。

自我评价:介绍的毫无逻辑,完全无重点,语速太快,这个是老毛病了,无奈,也不知道面试官到底听清楚我的话没。

面试官提问

问题一: 是给紧挨着span的p的写一个样式,但是不能起class的名字,要求兼容IE8。

好吧这个问题,其实我当时想的是不是有一个css的选择器中有一个紧挨着某个元素的那个选择器,当时我确不知道怎么写这个选择器了,我要查一下才知道。

后来,我还答了,给最外层起一个class,用last-of-type取得最后一个span,再用紧挨着的那个元素选择器。

<div>
   <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
   <p></p>
  <p></P>
</div>

回来查了一下,css选择器,紧挨着某个元素的选择器的的写法element+element。向下面这种写法。

span+p {
  color:red;
}

自我评价:我还大言不惭的说,css是我强项。---------结果我的强项,竟然是这样的,这样的,我还真好意思。。。。从这里可以看出我的基础还是不太扎实,对于不常用的,记不牢。就这样开始了,我想面试官,已经在心里对我说了,这就是你最强的。。。。呵呵呵。。。。应该不再对我抱有希望了,不过还是
继续对我提问,难为他了。

问题二:一个简单的javaScript闭包问题。可是我竟然紧张到外面的for循环了,只写了一个答案。在这里我竟然还是,我希望以后能专攻js,此时面试官的心里,应该是这样的,就你这样还js,呵呵呵。。。。

问题三:还问了你遇到最大的困难,你是怎样解决的?

问题四:es6和react的生命周期?

对于这个问题,前一天准备了一下,花了差不多两个小时把react的api,差不多就大致了解了一下,晚上的时候,把ES6常用的相关知识了解了一番-----let,const,class,super,extends,rest,export,import,字符串模板,
箭头函数,promise等等。这题过。

问题五:那关于ajax的跨域?

其实这个问题的都总结过了,但是我就是答不出来,归根究底就是自己不熟悉,不会。

问题六:你平时用什么抓包工具?

啊哈,什么?你说什么,抓包工具。。。。没用过。。。

**问题六:如果,给你一个场景,在webview中,你替换了一张图片,在浏览器中,查看出来的是新替换的那个图片,但是在用户手机中确是以前的那张图片,你怎样快速定位这个问题,造成的原因? **

啊。。。。什么?webview....我没用过,可能是缓存吧。但是你不可能让用户清理缓存啊。。。。所以不知道。。。我真的不知道啊。

问题七:关于框架?

你平时就只用过jquery是吧,嗯。是的,我平时的话,大部分只用这个,其他的用不到。。。。。。

问题八:你还学习PHP和数据库?

你php到了什么程度,啊,就会一点基础,那你的数据库了,会基本的增删改查了吗。。啊,我最近才了解基础,只会在控制台上面操作数据库呢。。。。

问题九:W3C的盒子模型和IE的盒子模型的区别?

啊,这个问题,我还是没特别注意过。那个对于宽度的设置更合理了。当然是W3C了,因为是规范的嘛。
回来查了一下:

W3C 盒子模型的范围包括 margin、border、padding、content,并且 content 部分不包含其他部分
。即宽度及content;
IE 盒子模型的范围也包括 margin、border、padding、content,和标准 W3C 盒子模型不同的是:IE 盒子模型的 content 部分包含了 border 和 pading。即宽度为:content+padding+border。

对这次面试的总结:我要是面试官,我也不会要我自己。就我这水平还想面试成功,呵呵呵呵。。。

  • 语速太快
  • 基础不牢
  • 先听清楚题目,再慢慢的回答
  • 框架用的不多
  • webview不会
  • php和数据库不太熟悉

怎么解决这个问题ctx.globalCompositeOperation='xor'

##怎么解决这个问题ctx.globalCompositeOperation='xor'啊##


<!DOCTYPE html>
<html>
<body>

<canvas id="myCanvas" width="300" height="400" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>

<script>

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.globalCompositeOperation="xor";

ctx.fillStyle="red";
ctx.beginPath();
ctx.moveTo(100,60);
ctx.lineTo(160,120);
ctx.lineTo(100,180);
ctx.lineTo(40,120);
ctx.closePath();
ctx.fill();


ctx.fillStyle="bule";
ctx.beginPath();
ctx.lineTo(160,120);
ctx.lineTo(100,180);
ctx.lineTo(40,120);
ctx.closePath();
ctx.fill();


</script>

</body>
</html>

qq20180915-0

图片Base64编码

前言

很早以前就听说过图片Base64编码,一直没有好好的了解,所以进行总结一下。

博文链接
知乎讨论

什么是base64编码?

图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址。

这样做有什么意义呢?我们知道,我们所看到的网页上的每一个图片,都是需要消耗一个 http 请求下载而来的。

没错,不管如何,图片的下载始终都要向服务器发出请求,要是图片的下载不用向服务器发出请求,而可以随着 HTML 的下载同时下载到本地那就太好了,而 base64 正好能解决这个问题。

base64编码的语法

在css里面的写法

#fkbx-spch, #fkbx-hspch {
  background: url(data:image/gif;base64,R0lGODlhHAAmAKIHAKqqqsvLy0hISObm5vf394uLiwAAAP///yH5B…EoqQqJKAIBaQOVKHAXr3t7txgBjboSvB8EpLoFZywOAo3LFE5lYs/QW9LT1TRk1V7S2xYJADs=) no-repeat center;
}

在html里面的写法

<img src="data:image/gif;base64,R0lGODlhHAAmAKIHAKqqqsvLy0hISObm5vf394uLiwAAAP///yH5B…EoqQqJKAIBaQOVKHAXr3t7txgBjboSvB8EpLoFZywOAo3LFE5lYs/QW9LT1TRk1V7S2xYJADs=">

当然 base64 编码不仅仅运用在图片编码,还可以:

thunder://QUFodHRwOi8vZG93bi5zYW5kYWkubmV0L3RodW5kZXI3L1RodW5kZXI3LjEuNS4yMTUyLmV4ZVpa

迅雷的“专用地址”也是用 Base64 加密的

那些适合图片使用 Base64 编码?

使用 base64 的一个前提,那就是被 base64 编码的图片足够尺寸小

图片被编码之后,生成的字符串编码大小一般而言都会比原文件稍大一些。即便 base64 编码能够被 gzip 压缩,压缩率能达到 50% 以上,想象一下,一个元素的 css 样式编写居然超过了 2000个 字符,那对 css 整体的可读性将会造成十分大的影响,代码的冗余使得在此使用 base64 编码将得不偿失。

如果图片足够小且因为用处的特殊性无法被制作成雪碧图(CssSprites),在整个网站的复用性很高且基本不会被更新。

背景图 background-image 。在很多地方,我们会制作一个很小的图片大概是几px*几px,然后平铺它页面当背景图。因为是背景图的缘故,所以无法将它放入雪碧图,而它却存在网站的很多页面,这种图片往往只有几十字节,却需要一个 http 请求,十分不值得。那么此时将它转化为 base64 编码,何乐而不为。

使用base64图片的弊端

而是因为如果把大图片编码到 html / css 中,会造成后者体积明显增加,明显影响网页的打开速度。
如果用外链图片的话,图片可以在页面渲染完成后继续加载,不会造成阻塞。

如果 base64 是被编码到 css/js 中,是可以缓存的,因为 css/js 文件可以缓存。

使用 base64 的另外一个弊端是 IE 的兼容性问题。IE 8 以下不支持 data url,IE 8 开始支持 data url,却有大小限制,32k(未测试)。

还有一个问题是,如果构建工具比较落后(或者没有构建工具),手动插入 base64 是很蛋疼的,器会卡到哭。

更便捷的将图片转化为Base64编码  

就是利用 Chrome 浏览器。在 chrome 下新建一个窗口,然后把要转化的图片直接拖入浏览器,打开控制台,点 Source,如下图所示,点击图片,右侧就会显示该图片的 base64 编码,是不是很方便。

base

SVG学习笔记之二

SVG中的坐标系统和坐标变换

  • SVG的世界、视野、视窗的概念
  • SVG中的图形分组
  • 坐标系统概述
  • 自身坐标系和参照坐标系
  • 坐标变换

SVG的世界、视野、视窗的概念

  • 世界是无穷大的
  • 视野是观察世界的一个矩形区域
  • width和height是控制视窗,浏览器用来渲染SVG内容区域的大小
  • SVG代码是定义世界的,SVG的内容的就是定义多少个矩形等等,就是世界
  • viewBox,preserveAspectRatio是控制视野,viewBox是规定观看SVG这个世界的矩形区域,preserveAspectRatio是规定视野是视窗的关系
<svg xmlns='http://www.w3.org/2000/svg' width='800' height='600' viewBox='0 0 400 300' viewBox,preserveAspectRatio='xMidYid meet'></svg>

初始坐标系

初始视窗坐标系是一个建立在视窗上的坐标系。原点(0,0)在视窗的左上角,X轴正向指向右,Y轴正向指向下,初始坐标系中的一个单位等于视窗中的一个"像素"。这个坐标系统类似于通过CSS盒模型在HTML元素上建立的坐标系。

初始用户坐标系是建立在SVG画布上的坐标系。这个坐标系一开始和视窗坐标系完全一样-它自己的原点位于视窗左上角,x轴正向指向右,y轴正向指向下。

使用viewBox属性,初始用户坐标系统-也称当前坐标系,或使用中的用户空间-可以变成与视窗坐标系不一样的坐标系。

到现在为止,我们还没有声明viewBox属性值。SVG画布的用户坐标系统和视窗坐标系统完全一样。

viewBox

viewBox生成的坐标系可以大于视窗也可以小于视窗,在视窗中可以整体可见或部分可见。其语法:

viewBox属性接收四个参数值,包括:<min-x>, <min-y>, width 和 height。

<min-x><min-y> 值决定viewBox的左上角,width和height决定视野的宽高。这里要注意视野的宽高不一定和父元素的宽高一样。<width><height>值为负数是不合法的。值为0的话会禁止元素的渲染。

注意视窗的宽度也可以在CSS中设置为任何值。例如:设置width:100%会让SVG视窗在文档中自适应。无论viewBox的值是多少,它会映射为外层SVG元素计算出的宽度值。

设置viewBox的例子如下:

<!-- The viewBox in this example is equal to the viewport, but it can be different -->
<svg width="800" height="600" viewBox="0 0 800 600">
    <!-- SVG content drawn onto the SVG canvas -->
</svg>

与viewport宽高比相同的viewBox:

<svg width="800" height="600" viewBox="0 0 400 300">
    <!-- SVG content drawn onto the SVG canvas -->
</svg>

这个例子中的viewBox的尺寸是视窗尺寸的一半。在这个例子中我们不改变viewBox的原点,所以<min-x><min-y>都设置成0。viewBox的宽高是viewport宽高的一半。这意味着我们保持宽高比。

所以,viewBox="0 0 400 300"到底有什么用呢?

  • 它声明了一个特定的区域,画布横跨左上角的点(0,0)到点(400,300)。
  • SVG图像被这个区域裁切。
  • 区域被拉伸(类似缩放效果)来充满整个视窗。
  • 用户坐标系被映射到视窗坐标系-在这种情况下----一个用户单位等于两个视窗单位。

现在让我们试着改变<min-x><min-y>的值。都设置为100。你可以设置成任何你想要的值。宽高比还是和视窗的宽高比一样。

<svg width="800" height="600" viewBox="100 100 200 150">
    <!-- SVG content drawn onto the SVG canvas -->
</svg>

用户坐标系被映射到视窗坐标系200用户单位映射为800视窗单位因此每个用户单位等于四个视窗单位。结果像你看到的那样是放大的效果。

另外注意,在这个时候,为<min-x><min-y>声明非0的值对图形有变换的效果;更加特别的是,SVG 画布看起来向上拉伸100个单位,向左拉伸100个单位(transform="translate(-100 -100)")。

的确,作为规范说明,“viewBox属性的影响在于用户代理自动添加适当的变换矩阵来把用户空间中具体的矩形映射到指定区域的边界(通常是视窗)”。

这是一个很棒的说明我们之前已经提到的内容的方法:图形被裁切然后被缩放以适应视窗。这个说明随后增加了一个注释:“在一些情况下用户代理在缩放变换之外需要增加一个移动变换。例如,在最外层的svg元素上,如果viewBox属性对<min-x><min-y>声明非0值得那么就需要移动变换。”

让我们试着给<min-x><min-y>添加-100。移动效果类似transform="translate(100 100)";这意味着图形会在切割和缩放后移动到右下方。添加新的无效<min-x><min-y>值,新的代码如下:

<svg width="800" height="600" viewBox="-100 -100 300 200">
    <!-- SVG content drawn onto the SVG canvas -->
</svg>

图形会在切割和缩放后移动到右下方,而对于非负值的viewBox的<min-x><min-y>则是先移动后缩放和切割

preserveAspectRatio属性强制统一缩放比来保持图形的宽高比。

如果你用不同于视窗的宽高比定义用户坐标系,如果像我们在之前的例子中看到的那样浏览器拉伸viewBox来适应视窗,宽高比的不同会导致图形在某些方向上扭曲。

preserveAspectRatio属性让你可以在保持宽高比的情况下强制统一viewBox的缩放比,并且如果不想用默认居中你可以声明viewBox在视窗中的位置。

语法:
preserveAspectRatio = defer <align> <meetOrSlice>

它在任何建立新viewport的元素上都有效。defer声明是可选的,并且只有当你在<image>上添加preserveAspectRatio才被用到。用在任何其他元素上时它都会被忽略。<images>本身不在这篇文章的讨论范围,我们暂时跳过defer这个选项。

align参数声明是否强制统一放缩,如果是,对齐方法会在viewBox的宽高比不符合viewport的宽高比的情况下生效。

如果align值设为none,例如:

preserveAspectRatio = "none"
图形不在保持宽高比而会缩放来适应视窗,像我们在上面两个例子中看到的那样。

其他所有preserveAspectRatio值都在保持viewBox的宽高比的情况下强制拉伸,并且指定在视窗内如何对齐viewBox。

最后一个属性,meetOrSlice也是可选的,默认值为meet。这个属性声明整个viewBox在视窗中是否可见。如果是,它和align参数通过一个或多个空格分隔。例如:

preserveAspectRatio = "xMinYMin slice"

这些值第一眼看起来也许很陌生。为了让它们更易于理解和熟悉,你可以把meetOrSlice的值类比于background-size的contain和cover值;它们非常类似。meet类似于contain,slice类似于cover。下面是每个值的定义和含义:

meet(默认值)
基于以下两条准侧尽可能缩放元素:

  • 保持宽高比
  • 整个viewBox在视窗中可见

在这个情况下,如果图形的宽高比不符合视窗,一些视窗会超出viewBox的边界(即viewBox绘制的区域会小于视窗)。在这个情况下,viewBox的边界被包含在viewport中使得边界满足。

这个值类似于background-size: contain。背景图片在保持宽高比的情况下尽可能缩放并确保它适合背景绘制区域。如果背景的长宽比和应用的元素的长宽比不一样,部分背景绘制区域会没有背景图片覆盖。

slice

在保持宽高比的情况下,缩放图形直到viewBox覆盖了整个视窗区域。viewBox被缩放到正好覆盖视窗区域(在两个维度上),但是它不会缩放任何超出这个范围的部分。换而言之,它缩放到viewBox的宽高可以正好完全覆盖视窗。

在这种情况下,如果viewBox的宽高比不适合视窗,一部分viewBox会扩展超过视窗边界(即,viewBox绘制的区域会比视窗大)。这会导致部分viewBox被切片。

你可以把这个类比为background-size: cover。在背景图片的情况中,图片在保持本身宽高比(如何)的情况下缩放到宽高可以完全覆盖背景定位区域的最小尺寸。

所以,meetOrSlice被用来声明viewBox是否会被完全包含在视窗中,或者它是否应该尽可能缩放来覆盖整个视窗,甚至意味着部分的viewBox会被“slice”。

align

align参数使用9个值中的一个或者为none。任何除none之外的值都用来保持宽高比缩放图片,并且还用来在视窗中对齐viewBox。

当使用百分比值时,align值类似于background-position。你可以把viewBox当做背景图像。通过align定位和background-position的不同在于,不同于通过一个与视窗相关的点来声明一个特定的viewBox值,它把具体的viewBox“轴”和对应的视窗的“轴”对齐。

为了理解每个align值的含义,我们将首先介绍每一个“轴”。

还记得viewBox的<min-x><min-y>值吗?我们将使用它们来定义viewBox中的"min-x"和"min-y"轴。另外,我们将定义两个轴“max-x”和”max-y“,各自通过<min-x> + <width><min-y> + <height>来定位。最后,我们定义两个轴"mid-x"和"mid-y",根据<min-x> + (<width>/2) 和 <min-y> + (<height>/2)来定位。

viewbox-x-y-axes

上面图片中的灰色虚线代表视窗的mid-x和mid-y轴。我们将对它们赋一些值来对齐viewBox的mid-x和mid-y轴。对于视窗,min-x的值等于0,min-y值也等于0,max-x值等于viewBox的宽度,max-y的值等于高度,mid-x和mid-y代表了宽度和高度的中间值。

对齐的取值包括:

none

不强制统一缩放。如果必要的话,在不统一(即不保持宽高比)的情况下缩放给定元素的图像内容直到元素的边界盒完全匹配是视窗矩形。

换句话说,如果有必要的话viewBox被拉伸或缩放来完全适应整个视窗,不管宽高比。图形也许会扭曲。

(注意:如果<align>的值是none,可选的<meetOrSlice>值无效。)

xMinYMin

  • 强制统一缩放
  • 视窗X轴的最小值对齐元素viewBox的<min-x>
  • 视窗Y轴的最小值对齐元素viewBox的<min-y>
  • 把这个类比为backrgound-position: 0% 0%;。

xMinYMid

  • 强制统一缩放。
  • 视窗X轴的最小值对齐元素viewBox的<min-x>
  • 视窗Y轴的中间值来对齐元素的viewBox的中间值。
  • 把这个类比为backrgound-position: 0% 50%;。

xMinYMax

  • 强制统一缩放。
  • 视窗X轴的最小值对齐元素viewBox的<min-x>
  • 视窗X轴的最大值对齐元素的viewBox的<min-y>+<height>
  • 把这个类比为backrgound-position: 0% 100%;。

xMidYMin

  • 强制统一缩放。

  • 视窗X轴的中间值对齐元素的viewBox的X轴中间值。

  • 视窗Y轴的中间值对齐元素的viewBox的<min-y>

  • 把这个类比为backrgound-position: 50% 0%;。

  • xMidYMid (默认值)

  • 强制统一缩放。

  • 视窗X轴的中间值对齐元素的viewBox的X轴中间值。

  • 视窗Y轴的中间值对齐元素的viewBox的Y轴中间值。

  • 把这个类比为backrgound-position: 50% 50%;。

xMidYMax

  • 强制统一缩放。
  • 视窗X轴的中间值对齐元素的viewBox的X轴中间值。
  • 视窗Y轴的最大值对齐元素的viewBox的<min-y>+<height>
  • 把这个类比为backrgound-position: 50% 100%;。

xMaxYMin

  • 强制统一缩放。
  • 视窗X轴的最大值对齐元素的viewBox的 <min-x>+<width>
  • 视窗Y轴的最小值对齐元素的viewBox的<min-y>
  • 把这个类比为backrgound-position: 100% 0%;。

xMaxYMid

  • 强制统一缩放。
  • 视窗X轴的最大值对齐元素的viewBox的 <min-x>+<width>
  • 视窗Y轴的中间值对齐元素的viewBox的Y轴中间值。
  • 把这个类比为backrgound-position: 100% 50%;。

xMaxYMax

  • 强制统一缩放。
  • 视窗X轴的最大值对齐元素的viewBox的 <min-x>+<width>
  • 视窗Y轴的最大值对齐元素的viewBox的 <min-y>+<height>
  • 把这个类比为backrgound-position: 100% 100%;。

所以,通过使用preserveAspectRatio属性的align和meetOrSlice值,你可以声明是否统一缩放viewBox,是否和视窗对齐,在视窗中是否整个可见。

参考文章和课程

SVG课程
SVG画布,坐标系统,视窗
理解SVG坐标系和变换:视窗,viewBox和preserveAspectRatio
Demo for article: SVG Coordinate Systems & Transformations (Part 1) – The viewport, viewBox, & preserveAspectRatio

制作SVG简易编辑器的笔记

字符串的拼接

function createHandler(shape, name, value) {
		var label = document.createElement('label');
		label.innerHTML = name + '<input type="range"  value="'+value+'" name="'+name+'" />';
		createShape.appendChild(label);
	}

使用+valName+

这个问题经常出错,需要好好的理解

RegExp对象的exec()方法

exec()方法专门为捕获组而设计的。该方法接受一个参数,即要应用模式的字符串,然后返回第一个匹配信息的数组;或者在没有匹配项的情况下返回null。

返回的数组虽然是Array的实例,但包含两个额外的属性:index和input。其中,index表示匹配项在字符串中的位置,而input表示应用正则表达式的字符串。

在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中捕获匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)

var text = 'mon and dad and baby';
var pattern = /mon( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
console.log(matches.index); //0;
console.log(matches.input); // 'mon and dad and baby'
console.log(matches[0]); //'mon and dad and baby'
console.log(matches[1]); //' and dad and baby'
console.log(matches[2]); //'and baby'

在这个例子中,模式包含了两个捕获组。最内部的捕获组匹配and baby,而包含它的捕获组匹配and dad
或者and dad and baby当把字符串传入exec()方法之后,发现了一个匹配项。因为整个字符串本身与模式匹配,所以返回的数组matches的index属性值为0,数组中的第一项是匹配的整个字符串,第二项包含与第一个捕获组匹配的内容,第三项包含与第二个捕获组匹配的内容。

text()方法

正则表达式test(),它接受一个字符串参数。在模式与该参数匹配的情况下返回true,否则,返回false

HTML5的oninput事件

在 Web 开发中经常会碰到需要动态监听输入框值变化的情况,如果使用 onkeydown、onkeypress、onkeyup 这个几个键盘事件来监测的话,监听不了右键的复制、剪贴和粘贴这些操作,处理组合快捷键也很麻烦。结合 HTML5 标准事件 oninput 和 IE 专属事件 onpropertychange 事件来监听输入框值变化则会变得相对于容易一些了。

oninput 是 HTML5 的标准事件,对于检测 textarea, input 这些元素通过用户界面发生的内容变化非常有用,在内容修改后立即被触发,不像 onchange 事件需要失去焦点才触发。oninput 事件在主流浏览器的兼容情况如下:

oninput 事件在 IE9 以下版本不支持,需要使用 IE 特有的 onpropertychange 事件替代,这个事件在用户界面改变或者使用脚本直接修改内容两种情况下都会触发,有以下几种情况:

  • 修改了 input:checkbox 或者 input:radio 元素的选择中状态, checked 属性发生变化。
  • 修改了 input:text 或者 textarea 元素的值,value 属性发生变化。
  • 修改了 select 元素的选中项,selectedIndex 属性发生变化。
  • 修改input其他类型,其属性值发生改变时。

在监听到 onpropertychange 事件后,可以使用 event 的 propertyName 属性来获取发生变化的属性名称。

集合 oninput & onpropertychange 监听输入框内容变化的示例代码如下:

<head>
    <script type="text/javascript">
    // Firefox, Google Chrome, Opera, Safari, Internet Explorer from version 9
        function OnInput (event) {
            alert ("The new content: " + event.target.value);
        }
    // Internet Explorer
        function OnPropChanged (event) {
            if (event.propertyName.toLowerCase () == "value") {
                alert ("The new content: " + event.srcElement.value);
            }
        }
    </script>
</head>
<body>
    Please modify the contents of the text field.
    <input type="text" oninput="OnInput (event)" onpropertychange="OnPropChanged (event)" value="Text field" />
</body>

最后需要注意的是:oninput 和 onpropertychange 这两个事件在 IE9 中都有个小BUG,那就是通过右键菜单菜单中的剪切和删除命令删除内容的时候不会触发,而 IE 其他版本都是正常的,目前还没有很好的解决方案。不过 oninput & onpropertychange 仍然是监听输入框值变化的最佳方案。

参考文章

实时监听输入框值变化的完美方案:oninput & onpropertychange

SVG学习笔记之九

SVG text

SVG文本是将图形和文本两者结合得最好的东西。它可以像其它图形元素一样渲染,这样你可以添加描边和填充,像图形、线条和箭头那样。它还可以被打包成XML字符数据,也就是说它是真的文本。

  • 当把SVG的标签里面的text内容放在html中的话,就能把文字像其它图形元素一样渲染。
  • 当单独存储为一个svg文件的话,就会渲染成一个xml文件。

SVG文本是可访问的。你可以选中它,复制,在其它位置粘贴。它可以被屏幕阅读器读取,也可以在搜索引擎中进行搜索。

你可以设置它在水平方向上排列,或者垂直都可以,还可以让文本沿着你创建的各种弯弯曲曲的路径排列。

EM框

在基于EM box的坐标系统中。每个字形的盒子都是1em高和1em宽。盒子内就是字形的设计空间,这个坐标系统也被称为设计空间坐标系统。

该空间给出具体的坐标,通过把EM框以em为单位分解成一定数量的单元。这个数量是字体的特征之一,被包括在字体表的信息中。

(0,0)点通常位于box的左边缘,但通常不是左下角。

罗马大写字母的底部通常是在y=0的位置。但有一些字母,如小写的g或y,在设计空间中仍然有一部分在下行,所以其y坐标有一部分是负值。

这导致y=0这个点变成了在设计空间的底部之上的某处(也就是说y=0不是最底部,它还有负值)。

SVG假设字体表将提供至少三种字体特征。

  • Ascent——从字体的(0,0)点到EM box顶部的距离
  • Descent——从字体的(0,0)点到EM box底部的距离
  • Baseline table(基线)——设计空间坐标系统中的基线的位置

em-box

text元素的X和Y属性----定位标准

使用<text>元素创建SVG文本,定义一个由文本组成的图形元素。因为文本的渲染方式和其它SVG元素一样,所以我们可以像给矩形、圆、线条这些图形增加内容一样给SVG文本添加修饰。

<svg width="660" height="220" style="outline: 1px solid red; overflow: visible;">  
  <text x="0" y="0">Some SVG Text</text>  
</svg>

上面的例子,我给SVG元素添加了width和height属性,设置了视窗的尺寸。我添加了一些内联样式,来展示视图的outline,这样我们可以看到我们的文本是否超出了视图的边界。我还把x和y的值改为0,这样文本就会在视窗的原点位置开始渲染。

注意文本显示的位置。你可能以为它会出现在视窗的左边缘,但你估计没想到它居然跑到了视图上面。如果我没有把overflow属性设置为visible,我们可能甚至都看不见它。

因为这里的y表示的是文本的基线位置(前面提到y=0并不是在设计空间最底部的位置),基线包含在字体表中。

svg_text

x为正值,会将文本移动到右边,y为正值会将它向下移。如果你想让别人看见你的文本,你需要设置y值为正。

关于x和y,你还可以设置文本的起始位置。每个可以接受一列坐标值(用逗号或空格分隔)。列中的第一个坐标表示文本的第一个字符的位置,第二个坐标表示文本的第二个字符的位置,以此类推。

<svg width="660" height="220" style="outline: 1px solid red; overflow: visible;">  
 <text x="0,20,40,60,70" y="20">This is some SVG Text</text>  
</svg>

这里我给出了一列x坐标。第一个坐标(0)表示字符T的位置,第二个坐标(20)表示字符h的x坐标,以此类推。一旦列的长度值都用完了,剩下的字符则按照原来的规则排列。

svg_text2

Text元素dx和dy属性----字形偏移

dx和dy属性,和x和y类似,除了它们的值表示的是相对于前一个字符的长度,而不是相对于整个视窗的绝对定位。

<svg width="660" height="220" style="outline: 1px solid red; overflow: visible;">  
  <text x="0" y="0" dx="10,20,30,40,60" dy="10,20,30,40,50,60">This is some SVG Text</text>  
</svg>

这里我把x和y的值都设置为0,然后dx和dy设置一列长度值。注意到随着长度值的增长,下一个字符的距离也在增长。如果想要在字符间保持相同的间距,你需要给dx和dy设置相同的值。

dx

间隔是按照字符(空格也是字符)来算的。

旋转SVG字符

<svg width="660" height="220" style="outline: 1px solid red; overflow: visible;">  
  <text x="0" y="20" rotate="0,45,0,90,180,0">This is some SVG Text</text>  
</svg>

这里我把x设置为0,y为20,给rotate属性设置了一列值。字符T旋转0deg,H旋转45deg,以此类推。直到这列值结束,所有剩下的字符都会保持旋转0deg。

和前面的实例一样,空格也会被旋转。this和is之间的空格被旋转了180deg,这就是为什么我在rotate这一列值的最后添加了一个0。否则,剩余的字符也都会被旋转180deg,变成颠倒的。

被旋转的是单个字符,而不是整个文本字符串。要旋转整个字符串你需要使用transform。

textLength属性

接受一个长度值,textLength属性允许你设置文本的长度为一个指定值,忽略容器的尺寸。即文本的长度大于容器的大度的话,文本不会被剪裁。会超出容器框。字符的会按照最大宽度的进行收缩或拉伸。

<svg width="660" height="220" style="outline: 1px solid red; overflow: visible;">  
  <text x="0" y="20" textLength="660">This is some SVG Text</text>  
</svg>

注意:字符之间的空间是根据textLength调整的,但是字符本身保留相同的尺寸(不会变形)。你可以通过针对textElement的属性来改变。

lengthAdjust属性

lengthAdjust属性接受两个值(spacing和spacingAndGlyphs),决定文本是否会被拉伸或压缩。

对于这两个值,spacing是默认值,这就是为什么前面的实例中,字符之间的空格自动调整为需要的长度。还是用前面的实例,只改变lengthAdjust的值。

<svg width="660" height="220" style="outline: 1px solid red; overflow: visible;">  
  <text x="0" y="20" textLength="660" lengthAdjust="spacingAndGlyphs">This is some SVG Text</text>  
</svg>

第一个为spacing是默认值

spacing

第二个为spacingAndGlyphs

spacingandglyphs

tspan元素

可以把元素当成SVG文本的span。你可以用来包裹SVG文本,来对它们做整体控制,关于显示和定位,文本片段之间的相互关系等。

<svg width="660" height="220" style="outline: 1px solid red; font-size: 2em; overflow: visible;">  
  <text x="240" y="120">  
    <tspan>SVG</tspan>  
    <tspan>SVG</tspan>  
  </text>  
</svg>

tspan中的文本按照内联顺序排列,和它们直接在text元素中显示的一样,中间有个空格。

可以给元素添加的属性也都可以添加给元素。我们来改变一下第二个tspan的位置,通过设置x和y坐标。这样我们可以把两个tspan分开。

我把第二个tspan移到了(10,10)的位置。因为x和y都是绝对定位,SVG 2的位置就移到了视窗的左边缘,也就是上边缘的下方。

为了相对定位,我们可以在下面的实例中使用dx和dy来定位。

<svg width="660" height="220" style="outline: 1px solid red; font-size: 2em; overflow: visible;">  
  <text x="240" y="120">  
    <tspan >SVG 1</tspan>  
    <tspan dx="50,10,10,0,5" dy="50,10,10,10">SVG 2</tspan>  
  </text>  
</svg>

所有你给text元素应用的属性,都会传播到tspan元素中。如果你不希望继承某个属性值,你可以在tspan中给它设置新的值,覆盖掉原来text元素中的值。

参考文章

如何使用SVG Text
如何操作SVG Text
如何使用tspan元素给SVG文本添加样式、定位

HTML5的File--上传用户选择的文件的例子

上传用户选择的文件

异步的将用户所选择的文件上传到服务器上(比如一张图片)。

创建上传任务

紧接上面构建缩略图例子中的代码,需要重申的是 每一个略缩图都在 CSS 类 obj 中,而其相应的 File 则是在 file 属性里。这样子,使用 Document.querySelectorAll() 我们就能很容易选择所有用户想要上传的图片:

function sendFiles() {
//寻找所有的缩略图
  var imgs = document.querySelectorAll(".obj");
 //遍历所有的缩略图
  for (var i = 0; i < imgs.length; i++) {
    new FileUpload(imgs[i], imgs[i].file);
  }
}

旦我们有了这个列表,遍历该列表并为每一个元素创建一个 FileUpload 实例。每个函数都会上传相应的文件。

实现文件上传

FileUpload 函数接受两个参数:一个 img 元素,一个可以从中读取到图像数据的文件 file。

function FileUpload(img, file) {
  var reader = new FileReader();  
//活动指示器(throbber),其是用于显示相关的进度信息
  this.ctrl = createThrobber(img);
  var xhr = new XMLHttpRequest();
  this.xhr = xhr;
  
  var self = this;
  this.xhr.upload.addEventListener("progress", function(e) {
        if (e.lengthComputable) {
          var percentage = Math.round((e.loaded * 100) / e.total);
          self.ctrl.update(percentage);
        }
      }, false);
  
  xhr.upload.addEventListener("load", function(e){
          self.ctrl.update(100);
          var canvas = self.ctrl.ctx.canvas;
          canvas.parentNode.removeChild(canvas);
      }, false);
  xhr.open("POST", 
    "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");
  xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
  reader.onload = function(evt) {
    xhr.sendAsBinary(evt.target.result);
  };
  reader.readAsBinaryString(file);
}

上面显示的 FileUpload() 函数创建了一个活动指示器(throbber),其是用于显示相关的进度信息,然后又创建了一个 XMLHttpRequest 去处理上传到服务器的数据。

在实际传输数据之前,需要完成几个步骤:

  • 设置 XMLHttpRequest 的 upload progress 监听器以新的百分比信息去更新活动指示器(throbber),并将其作为上传进度,而活动指示器将基于最新的信息进行更新。

  • 设置 XMLHttpRequest 的 upload load 事件句柄更新信息活动指示器的进度直到其为100%(避免过程中的 granularity quirks)。上传完成后活动指示器就会消失。

  • 通过调用的 XMLHttpRequest 的 open() 方法生成一个 POST 请求以其启动图像文件上传。

  • MIME 类型上传是通过调用 XMLHttpRequest 的函数 overrideMimeType() 设置。在这种情况下,我们使用一个通用的 MIME 类型;你可以选择不设置 MIME 类型,这取决于你的使用情况。

  • FileReader 对象可以把 file 转换成二进制字符串。

  • 最后,当内容载入完毕的时候就会调用 XMLHttpRequest 的 sendAsBinary() 函数去上传 file 的内容。

异步实现文件上传

<?php
if (isset($_FILES['myFile'])) {
    // Example:
    move_uploaded_file($_FILES['myFile']['tmp_name'], 
        "uploads/" . $_FILES['myFile']['name']);
    exit;
}
?><!DOCTYPE html>
<html>
<head>
    <title>dnd binary upload</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script type="text/javascript">
        function sendFile(file) {
            var uri = "/index.php";
            var xhr = new XMLHttpRequest();
            var fd = new FormData();
            
            xhr.open("POST", uri, true);
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    // Handle response.
                    alert(xhr.responseText); // handle response.
                }
            };
            fd.append('myFile', file);
            // Initiate a multipart/form-data upload
            xhr.send(fd);
        }

        window.onload = function() {
            var dropzone = document.getElementById("dropzone");
            dropzone.ondragover = 
              dropzone.ondragenter = function(event) {
                event.stopPropagation();
                event.preventDefault();
            }
    
            dropzone.ondrop = function(event) {
                event.stopPropagation();
                event.preventDefault();

                var filesArray = event.dataTransfer.files;
                for (var i=0; i<filesArray.length; i++) {
                    sendFile(filesArray[i]);
                }
            }
    </script>
</head>
<body>
    <div>
        <div id="dropzone" 
          style="margin:30px; width:500px; height:300px; border:1px dotted grey;">
            Drag & drop your file here...
        </div>
    </div>
</body>
</html>

使用URLs对象显示PDF

URLs对象不仅仅是可用于图像!它们可用于显示嵌入的PDF文件,或可以由浏览器显示的任何其它资源。

在火狐中,使PDF出现在内嵌的iframe中(并不建议作为一个下载的文件),把偏好pdfjs.disabled设置为false(这个还没有成为标准)

<iframe id="viewer">
var obj_url = window.URL.createObjectURL(blob);
var iframe = document.getElementById('viewer');
iframe.setAttribute('src', obj_url);
window.URL.revokeObjectURL(obj_url);

其他文件类型使用URLs对象

你可以用同样的方式操纵其它格式的文件。下面是如何预览上传的视频

var video = document.getElementById('video');
var obj_url = window.URL.createObjectURL(blob);
video.src = obj_url;
video.play()
window.URL.revokeObjectURL(obj_url);

参考文章

在web应用中使用文件

绘制一轮弯月

原理

moon

  • 三角函数的正切函数:tan=对边/邻边

  • 两点间的距离公式:|AB|=√[(x1-x2)^2+ (y1-y2)^2] 在这里求的是AC的长(用勾股定理同样可以得到AH²+HC²=AC²)

  • 先用arc()方法绘制0.5π-1.5π逆时针的半圆;

  • 再用moveTo()方法移动到位置点A(400,100)

  • 再用arcTo()方法创建内弧

	var canvas = document.getElementById('moon');
	// 不支持的浏览器直接不显示
	if (!canvas || !canvas.getContext) {
		alert("您的浏览器太旧了,升级您的浏览器");
		return false;
	}
	var	context = canvas.getContext('2d'),
		cw = 800,
		ch = 800;
		canvas.width = cw;
		canvas.height = ch;

		// 绘制弯月
		context.arc(400, 400, 300, 0.5*Math.PI, 1.5*Math.PI, true);
		context.moveTo(400, 100);
		context.arcTo(1200, 400, 400, 700, (400-100)*dis(400, 100, 1200, 400)/(1200 - 400));
		context.stroke();

	function dis(x1, y1, x2, y2) {
		return Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
	}

封装的一轮弯月

;(function(){
	var canvas = document.getElementById('moon');
	// 不支持的浏览器直接不显示
	if (!canvas || !canvas.getContext) {
		alert("您的浏览器太旧了,升级您的浏览器");
		return false;
	}
	var	context = canvas.getContext('2d'),
		cw = 800,
		ch = 800;
		canvas.width = cw;
		canvas.height = ch;

	fillMoon(context, 2, 400, 400, 300, 0);

	// fillMoon
	function fillMoon(ctx, d, x, y, R, rotate, fillColor) {
		ctx.save();
		ctx.translate(x, y);
		ctx.rotate(rotate*Math.PI/180);
		ctx.scale(R, R);
		pathMoon(ctx, d);
		ctx.fillStyle = fillColor || '#fb5';
		ctx.fill();
		ctx.restore();
	}
	// 绘制圆弧的路径
	function pathMoon(ctx, d) {
		ctx.beginPath();
		// 外圆弧
		ctx.arc(0, 0, 1, 0.5*Math.PI, 1.5*Math.PI, true);
		// 内圆弧
		ctx.moveTo(0, -1);
		ctx.arcTo(d, 0, 0, 1, dis(0, -1, d, 0)/d);
		ctx.closePath();
	}

	function dis(x1, y1, x2, y2) {
		return Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
	}
})()

贝塞尔区域

这个是贝塞尔曲线的交互效果的预览函数

An interactive example of the canvas quadraticCurveTo function

quadraticCurveTo() 二次贝塞尔曲线

  • quadraticCurveTo() 方法通过使用表示二次贝塞尔曲线的指定控制点,向当前路径添加一个点。
    二次贝塞尔曲线需要两个点。

  • 第一个点是用于二次贝塞尔计算中的控制点,第二个点是曲线的结束点。曲线的开始点是当前路径中最后一个点。

  • 如果路径不存在,那么请使用 beginPath() 和 moveTo() 方法来定义开始点。

img_quadraticcurve

开始点: moveTo(20,20)
控制点: quadraticCurveTo(20,100,200,20)
结束点: quadraticCurveTo(20,100,200,20)

bezierCurveTo() 方法通过使用表示三次贝塞尔曲线。

ezierCurveTo() 方法通过使用表示三次贝塞尔曲线的指定控制点,向当前路径添加一个点。三次贝塞尔曲线需要三个点。

  • 前两个点是用于三次贝塞尔计算中的控制点,第三个点是曲线的结束点。

  • 曲线的开始点是当前路径中最后一个点, 如果路径不存在,那么请使用 beginPath() 和 moveTo() 方法来定义开始点。

img_beziercurve

开始点: moveTo(20,20)
控制点 1: bezierCurveTo(20,100,200,100,200,20)
控制点 2: bezierCurveTo(20,100,200,100,200,20)
结束点: bezierCurveTo(20,100,200,100,200,20)

html5桌面通知

HTML5 Web Notification语法

window.Notification

如果浏览器支持Web Notification,不考虑私有前缀,则window.Notification就会是一个有很多静态属性和实例方法的函数。基本上,Web Notification所有的语法都是围绕Notification这个函数来进行的。

HTML5推出了桌面通知功能。顾名思义,这个通知看起来就跟其他的桌面通知一样,比如QQ的弹窗。因为没有在浏览器中展示,所以看起来似乎与浏览器没有关系,但其是靠浏览器运行的。这就是Notification。

判断浏览器是否支持桌面通知

if (!!window.Notification) {
    var notification = window.Notification;
    var mynotify;
    if (notification.permission == "granted") {
        //创建通知
        showmsg();
    }
    //判断许可状态
    else if (notification.permission == "default") {
        /*
        如果用户从未设置过此网站的桌面提醒状态(可能是第一次访问这个网站,或者以前允许过,但是在通知-例外中删除掉了),则调用requestPermission方法,让用户选择是否允许桌面提醒
        */
        notification.requestPermission(function(permission) {
            //在回掉函数中判断用户的选择,在这里不用为“拒绝”选项编写代码,因为既然拒绝,就什么都不做了,也不用为默认状态编写代码,因为既然已经弹出让用户选择的选项了,就没有所谓的默认状态了。所以只需要处理用户允许的状态就可以了
            if (permission == "granted") {
                //创建通知
                showmsg();
            }
        });
    }
}
Notification.requestPermission()

来确定用户是否同意,语法目前有新旧两种,下面这个是最近规范上更新的基于promise的语法:

Notification.requestPermission().then(function(permission) { ... });
//下面这个是基于简单的回调:
Notification.requestPermission(callback);

无论是then中的还是直接callback函数的参数都是一样的,表示当前是否允许。只会是granted, denied, 或default.

  • default:未设置过为这个状态,通过Notification.requestPermission()可以询问用户是否允许通知。
  • granted:用户点击允许后的状态
  • denied: 用户点击拒绝后的状态,通知框不可用

Notification特点

  • 是否显示通知需要用户确认
  • 通知页面即使不是当前激活页面,也可显示此页面的通知
  • 通知是依附于浏览器的,浏览器关闭,通知也关闭
  • 通知并非显示在浏览器右下角,而是现实在桌面的右下角,更像是C/S程序

创建通知

用户允许桌面通知后,就要构造桌面通知,其语法为:

new Notification(title, options)

通过new构造,显示通知。其中title是必须参数,表示通知小框框的标题内容,options是可选参数,对象,支持的参数以及释义见下表:

属性名 释义
dir 默认值是auto, 可以是ltr或rtl,有点类似direction属性。表示提示主体内容的水平书写顺序。
lang 提示的语言。没看出来有什么用。大家可以忽略之~
body 提示主体内容。字符串。会在标题的下面显示。比方说上面的“好啊!(害羞.gif)”。
tag 字符串。标记当前通知的标签。
icon 字符串。通知面板左侧那个图标地址。
data 任意类型和通知相关联的数据。
vibrate 通知显示时候,设备震动硬件需要的振动模式。所谓振动模式,指的是一个描述交替时间的数组,分别表示振动和不振动的毫秒数,一直交替下去。例如[200, 100, 200]表示设备振动200毫秒,然后停止100毫秒,再振动200毫秒。
renotify 布尔值。新通知出现的时候是否替换之前的。如果设为true,则表示替换,表示当前标记的通知只会出现一个。注意都这里“当前标记”没错,true参数要想其作用,必须tag需要设置属性值。然后,通知就会像这样覆盖,而不会是默认的叠高楼:
silent 布尔值。通知出现的时候,是否要有声音。默认false, 表示无声。
sound 字符串。音频地址。表示通知出现要播放的声音资源。
noscreen 布尔值。是否不再屏幕上显示通知信息。默认false, 表示要在屏幕上显示通知内容。
sticky 布尔值。是否通知具有粘性,这样用户不太容易清除通知。默认false, 表示没有粘性。根据我自己的猜测,应该和position的sticky属性值类似。

Notification.close()

关闭显示的通知,可以使用该方法。实际上,通知如果你放着不管,一段时间后就会自动隐藏。

相关事件

Notification.onclick

点击通知后发生的事件

Notification.onerror

通知显示异常发生的事件

Notification.onclose

通知关闭了,然后…… 无论是用户手动关闭,还是直接Notification.close()关闭都会触发该该事件。

Notification.onshow

通知显示时发生的事件

function showmsg() {
    // 位于桌面左下角的通知,类型桌面应用
    mynotify = new Notification("会议提醒", {
        body: '您计划于今天下午4点召开全体会议,请准时参加',
        icon: 'http://q4.qlogo.cn/g?b=qq&k=icUjVAN5Ja7BCDQ1ICl8Svw&s=40',
        tag: 1
    });
    // 通知显示的时候执行的函数
    mynotify.onshow = function() {
        setTimeout(function() {
            mynotify.close();
        }, 5000);
    }
    // 点击这个通知,执行的函数
    mynotify.onclick = function() {
        window.location.href = "http://www.baidu.com";
    }
    // 通知在桌面左下消失的时候,浏览器页面的执行的函数
    mynotify.onclose = function() {
        //可以在这里做一些有意义的事情,比如记录显示通知的次数
        document.write('ss');
    }
}

参考文章

html5桌面通知
简单了解HTML5中的Web Notification桌面通知

HTML DOM classList 属性

定义和用法

classList 属性返回元素的类名,作为 DOMTokenList 对象。
该属性用于在元素中添加,移除及切换 CSS 类。
classList 属性是只读的,但你可以使用 add() 和 remove() 方法修改它。

IE10才支持

方法和属性

length:返回类列表中类的数量,该属性是只读的

方法 描述
add(class1, class2, ...) 在元素中添加一个或多个类名。如果指定的类名已存在,则不会添加
contains(class) 返回布尔值,判断指定的类名是否存在。可能值:true - 元素包已经包含了该类名false - 元素中不存在该类名
item(index) 返回类名在元素中的索引值。索引值从 0 开始。如果索引值在区间范围外则返回 null
remove(class1, class2, ...) 移除元素中一个或多个类名。注意: 移除不存在的类名,不会报错。
toggle(class, true/false) 在元素中切换类名。

例子

<div id="myDIV">我是内容</div>
<button id="btn">点击切换</button>
<script>
//增加class
document.getElementById("myDIV").classList.add("mystyle", "anotherClass", "thirdClass");
//移除class
 document.getElementById("myDIV").classList.remove("mystyle", "anotherClass", "thirdClass");
 var x = document.getElementById("myDIV").classList.item(0);
 console.log(x); //mystyle
 var y = document.getElementById("myDIV").classList.contains("mystyle");
 console.log(y); //true
//toggle
document.getElementById('btn').addEventListener('click',function(){
  document.getElementById("myDIV").classList.toggle("newClassName");
 }, false);
//var q = document.getElementById("myDIV").classList.length;
console.log(q);
</script>

参考文章

HTML DOM classList 属性

Canvas中的非零环绕规则

非零环绕规则

fei0

非零环绕规则

对于路径中指定范围区域,从该区域内部画一条足够长的线段,使此线段的完全落在路径范围之外。

非零环绕规则计数器

然后,将计数器初始化为0,每当这个线段与路径上的直线或曲线相交时,就改变计数器的值,

如果是与路径顺时针相交时,那么计数器就加1, 如果是与路径逆时针相交时,那么计数器就减1.

如果计数器始终不为0,那么此区域就在路径范围里面,在调用fill()方法时,浏览器就会对其进行填充。

如果最终值是0,那么此区域就不在路径范围内,浏览器就不会对其进行填充。

从上图中看

  • 第一条线段:

根据非零环绕规则,这条直线只经过路径一次且路径是逆时针方向,那么计数器为-1;根据浏览器对计数器的计算原理得出,当调用fill()方法时浏览器会填充此区域。

  • 第二条线段:

根据非零环绕规则,这条直线经过路径二次且路径都是逆时针方向,那么计数器为-2;根据浏览器对计数器的计算原理得出,当调用fill()方法时浏览器会填充此区域。

  • 第三条线段:

根据非零环绕规则,这条直线经过路径二次;第一次经过的路径是逆时针方向,计数器则为-1; 第二次经过的路径是顺时针方向,计数器为1;原因计数器的最终值为0-1+1 = 0;所以根据浏览器对计数器的计算原理得出,当调用fill()方法时浏览器不会填充此区域。

一个圆环的例子

var canvas = document.getElementById('spotlight'),
	ctx = canvas.getContext('2d') ;
	ctx.fillStyle = 'red';
	ctx.arc(400, 400, 200, 0, Math.PI*2, false);
	ctx.arc(400, 400, 300, 0, Math.PI*2, true);
	ctx.fill();

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.