canvas达成弧形可拖动进程条效果,完结放大镜效果

图形放大镜

图表放大镜

1、效果如下:

简介:

canvas是html5最注重的要素,提供了二个画布的遵守,有庞大的图形管理本事

澳门葡京 1

想想导图

效果

澳门葡京 2
在线演示
  
源码

效果

澳门葡京 3
在线演示
  
源码

澳门葡京 4

canvas标签增添

canvas达成弧形可拖动进程条效果,完结放大镜效果。要选取canvas,首先要在body中增多上canvas标签

canvas标签是一块暗中同意300*150大小的画布

原理

第3采纳图片的1块区域,然后将那块区域推广,然后再绘制到原来的图形上,保障两块区域的中央点一致,
如下图所示:
澳门葡京 5

原理

率先采取图片的一块区域,然后将那块区域加大,然后再绘制到原来的图纸上,保障两块区域的中央点壹致,
如下图所示:
澳门葡京 6

二、

画布宽高设置:

应用canvas标签的脾性,width及height直接在行间设置

那里注意:不要用css样式去改画布大小,那么些会招致画布中的图像被拉伸

<canvas id="canvas1" width="800" height="500"></canvas>

初始化

<canvas id="canvas" width="500" height="500">
</canvas>

<img src="image.png" style="display: none" id="img">

获得 canvas 和 image 对象,那里运用 <img> 标签预加载图片,
关于图片预加载可以看这里

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var img = document.getElementById("img");

安装相关变量

// 图片被放大区域的中心点,也是放大镜的中心点
var centerPoint = {};
// 图片被放大区域的半径
var originalRadius = 100;
// 图片被放大区域
var originalRectangle = {};
// 放大倍数
var scale = 2;
// 放大后区域
var scaleGlassRectangle

初始化

<canvas id="canvas" width="500" height="500">
</canvas>

<img src="image.png" style="display: none" id="img">

得到 canvas 和 image 对象,那里运用 <img> 标签预加载图片,
关于图片预加载可以看这里

var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var img = document.getElementById("img");

设置相关变量

// 图片被放大区域的中心点,也是放大镜的中心点
var centerPoint = {};
// 图片被放大区域的半径
var originalRadius = 100;
// 图片被放大区域
var originalRectangle = {};
// 放大倍数
var scale = 2;
// 放大后区域
var scaleGlassRectangle

正文是落成可拖动滑块完成的基本思路,及1个简约的dome,()

画布css样式

画布的别的css样式能够常常设置

canvas标签中的内容
canvas标签中的内容是(包蕴标签)唯有在不援救此功能的低版本浏览器中才会呈现的;在高版本浏览器中不会展现出来

<canvas id="canvas1" width="800" height="500">
    只是一个canvas标签,然而你看不到
</canvas>

画背景图片

function drawBackGround() {
    context.drawImage(img, 0, 0);
}

画背景图片

function drawBackGround() {
    context.drawImage(img, 0, 0);
}

三、

获取canvas标签及上下文

canvas标签使用js平常dom节点的得到形式获得

var canvas = document.getElementById('canvas1');

上下文是记录canvas实践命令语句的地点,获取如下,前边的参数今后唯有二d可选

var context = canvas.getContext('2d');

总计图片被推广的区域的界定

此间我们利用鼠标的岗位作为被推广区域的焦点点(放大镜随着鼠标移动而移动),因为
canvas
在画图片的时候,必要精晓左上角的坐标以及区域的宽高,所以那里大家总计区域的范围

function calOriginalRectangle(point) {
    originalRectangle.x = point.x - originalRadius;
    originalRectangle.y = point.y - originalRadius;
    originalRectangle.width = originalRadius * 2;
    originalRectangle.height = originalRadius * 2;
}

测算图片被加大的区域的范围

此地大家选择鼠标的职位作为被加大区域的着力点(放大镜随着鼠标移动而活动),因为
canvas
在画图片的时候,须求领会左上角的坐标以及区域的宽高,所以这里大家总括区域的范围

function calOriginalRectangle(point) {
    originalRectangle.x = point.x - originalRadius;
    originalRectangle.y = point.y - originalRadius;
    originalRectangle.width = originalRadius * 2;
    originalRectangle.height = originalRadius * 2;
}

一、首先在html中创制多个canvas标签

canvas坐标系

左上角是00点,x轴正方向向右,y轴正方向向下

澳门葡京 7

制图放大镜区域

制图放大镜区域

<canvas id="canvas"  width="400" height="400"></canvas>

体制设置

边框设置:context.strokeStyle
填充设置:context.fillStyle
线宽设置:context.lineWidth

context.strokeStyle = "red";
context.lineWidth = 2;
context.fillStyle = 'yellow';

剪裁区域

放大镜一般是圈子的,那里大家应用 clip
函数裁剪出一个圆形区域,然后在该区域中绘制放大后的图。一旦减少了有些区域,以往全数的绘图都会被界定的这么些区域里,那里大家接纳
saverestore 方法清除裁剪区域的熏陶。save
保存当前画布的一遍状态,蕴含 canvas 的上下文属性,例如
stylelineWidth 等,然后会将那么些景况压入贰个储藏室。restore
用来回复上三次 save 的动静,从饭馆里弹出最顶层的气象。

context.save();
context.beginPath();
context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false);
context.clip();
......
context.restore();

剪裁区域

放大镜一般是圈子的,那里大家使用 clip
函数裁剪出2个圆形区域,然后在该区域中绘制放大后的图。一旦裁减了有个别区域,现在全部的绘图都会被限定的那个区域里,那里大家利用
saverestore 方法清除裁剪区域的影响。save
保存当前画布的3遍状态,包蕴 canvas 的内外文属性,比方
stylelineWidth 等,然后会将以此情形压入贰个仓库。restore
用来回复上一回 save 的气象,从饭馆里弹出最顶层的景观。

context.save();
context.beginPath();
context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false);
context.clip();
......
context.restore();

二、创造七个进程条对象,编写开首化方法,获取canvas对象及上下文情状;event方法是用来绑定事件(具体后边介绍);draw是用来绘图的办法,那里把Draw对象的全方位措施赋给draw方法;创造绘图实例p,绘制开始图形;

绘图图形

算算放大镜区域

透过骨干点、被放大区域的宽高以及放大倍数,获得区域的左上角坐标以及区域的宽高。

scaleGlassRectangle = {
    x: centerPoint.x - originalRectangle.width * scale / 2,
    y: centerPoint.y - originalRectangle.height * scale / 2,
    width: originalRectangle.width * scale,
    height: originalRectangle.height * scale
}

估测计算放大镜区域

经过主旨点、被推广区域的宽高以及放大倍数,获得区域的左上角坐标以及区域的宽高。

scaleGlassRectangle = {
    x: centerPoint.x - originalRectangle.width * scale / 2,
    y: centerPoint.y - originalRectangle.height * scale / 2,
    width: originalRectangle.width * scale,
    height: originalRectangle.height * scale
}
var Draw={
 init:function(){
 this.obj=document.getElementById("canvas"); //获取canvas对象
 this.cObj=document.getElementById("canvas").getContext("2d");//获取canvas对象上下文环境
 this.event(); //初始化事件
 this.pathr=120; //滑动路径半径
 this.draw.prototype=this; //draw继承Draw方法
 this.p=new this.draw(112,284,18); //创建实例p
  } 
  //... 
}
绘图命令
  1. 边框式绘制:context.stroke();
  2. 填充式绘制:context.fill();

制图图片

在那里我们接纳
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height); 方法,将
canvas
自个儿作为1副图片,然后取被放大区域的图像,将其绘制到放大镜区域里。

context.drawImage(canvas,
    originalRectangle.x, originalRectangle.y,
    originalRectangle.width, originalRectangle.height,
    scaleGlassRectangle.x, scaleGlassRectangle.y,
    scaleGlassRectangle.width, scaleGlassRectangle.height
);

绘图图片

在那边大家采纳
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height); 方法,将
canvas
本身作为一副图片,然后取被推广区域的图像,将其绘制到放大镜区域里。

context.drawImage(canvas,
    originalRectangle.x, originalRectangle.y,
    originalRectangle.width, originalRectangle.height,
    scaleGlassRectangle.x, scaleGlassRectangle.y,
    scaleGlassRectangle.width, scaleGlassRectangle.height
);

三、在Draw中编辑绘图方法draw绘制下图:

新建路线beginPath()

本条点子表示重新开头一段路线,也能够清楚为新建多个图层
其一的办法之后试行的绘图动作,不会实行到那么些点子以前的通令

context.beginPath();

绘图放大边缘

createRadialGradient 用来绘制渐变图像

context.beginPath();
var gradient = context.createRadialGradient(
    centerPoint.x, centerPoint.y, originalRadius - 5,
    centerPoint.x, centerPoint.y, originalRadius);
gradient.addColorStop(0, 'rgba(0,0,0,0.2)');
gradient.addColorStop(0.80, 'silver');
gradient.addColorStop(0.90, 'silver');
gradient.addColorStop(1.0, 'rgba(150,150,150,0.9)');

context.strokeStyle = gradient;
context.lineWidth = 5;
context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false);
context.stroke();

绘制放大边缘

createRadialGradient 用来绘制渐变图像

context.beginPath();
var gradient = context.createRadialGradient(
    centerPoint.x, centerPoint.y, originalRadius - 5,
    centerPoint.x, centerPoint.y, originalRadius);
gradient.addColorStop(0, 'rgba(0,0,0,0.2)');
gradient.addColorStop(0.80, 'silver');
gradient.addColorStop(0.90, 'silver');
gradient.addColorStop(1.0, 'rgba(150,150,150,0.9)');

context.strokeStyle = gradient;
context.lineWidth = 5;
context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false);
context.stroke();

澳门葡京 8

制图直线:
  1. 设置画笔开头地方:context.moveTo(x,y)
  2. 设置画笔停止地方:context.lineTo(x,y)
  3. 线条两边圆角:context.lineCap = "round"

context.strokeStyle = "red";
context.lineWidth = 2;
context.moveTo(100,100);
context.lineTo(500,100);
context.stroke();

澳门葡京 9

增添鼠标事件

为 canvas 增多鼠标移动事件

canvas.onmousemove = function (e) {
    ......
}

增添鼠标事件

为 canvas 增多鼠标移动事件

canvas.onmousemove = function (e) {
    ......
}

(1)成立绘图方法,获取参数

制图三角:方法正是绘制三条直线

制图多条连接线段时,不必每一次重复定义起首点
画笔会停在上1段线段的顶峰,并默以为下1遍绘制的开首点

context.strokeStyle = "red";
context.lineWidth = 2;

context.moveTo(10,10);
context.lineTo(10,110);
context.lineTo(110,110);
context.lineTo(10,10);
context.stroke();

瞩目,此处用绘制直线的点子,进行图片的绘图时;直线封闭处汇合世毛病,不会全盘关闭

澳门葡京 10

澳门葡京 11

调换坐标

鼠标事件获得坐标一般为荧屏的只怕 window 的坐标,大家须求将其装换为
canvas 的坐标。getBoundingClientRect
用于获取页面中有个别成分的左,上,右和下分别针锋绝对浏览器视窗的地方。

function windowToCanvas(x, y) {
    var bbox = canvas.getBoundingClientRect();
    return {x: x - bbox.left, y: y - bbox.top}
}

转移坐标

鼠标事件得到坐标一般为荧屏的仍然 window 的坐标,我们供给将其装换为
canvas 的坐标。getBoundingClientRect
用于获取页面中有个别成分的左,上,右和下各自针锋相对浏览器视窗的岗位。

function windowToCanvas(x, y) {
    var bbox = canvas.getBoundingClientRect();
    return {x: x - bbox.left, y: y - bbox.top}
}
draw:function(x,y,r,j){ //绘图
 this.cObj.clearRect(0,0,400,400); //清空画布
 this.x=x; //滑块坐标x
 this.y=y; //滑块坐标y 
 this.r=r; //滑块移动路径半径
 this.j=j; //橙色圆弧结束弧度值
 //...
}
关闭方法:context.closePath()

丰裕地点的密闭命令后,闭合处的欠缺就会被修复
本条方法会自动将起源与末点相连,所以不必再将路线回到起首点

context.strokeStyle = "red";
context.lineWidth = 2;

context.beginPath();
context.moveTo(10,10);
context.lineTo(10,110);
context.lineTo(110,110);
context.closePath();  //封闭路径后最后一个点就不用定义了
context.stroke();

澳门葡京 12

澳门葡京 13

修改鼠标准样品式

我们得以经过 css 来修改鼠标样式

#canvas {
    display: block;
    border: 1px solid red;
    margin: 0 auto;
    cursor: crosshair;
}

修改鼠标准样品式

我们得以经过 css 来修改鼠标准样品式

#canvas {
    display: block;
    border: 1px solid red;
    margin: 0 auto;
    cursor: crosshair;
}

(二)绘制内侧圆弧

绘图矩形

概念矩形:context.rect(x,y,w,h);

  1. x:起首地点x坐标(矩形左上角)
  2. y:开头地点y坐标(矩形左上角)
  3. w:矩形宽
  4. h:矩形高

直白绘制:省去了概念矩形的步骤,依据定义的体裁直接填充或描边

context.fillRect(x,y,w,h)
context.strokeRect(x,y,w,h)

橡皮擦成效:clearRect(x,y,w,h)

  1. 擦除矩形范围以内的内容
  2. 常用来擦除整套画布以落成动画效果

图片放大镜

我们只怕基于 canvas
绘制一些图形或然图像,借使多个要素的坐标离得比较近,就会给成分的选拔带来一些影响,比如大家画两条线,三个线的坐标是(200.5, 400) -> (200.5, 200),另八个线的坐标为
(201.5, 400) -> (201.5, 20),那么那两条线差不多就会重叠在联合,如下图所示:
澳门葡京 14

使用图表放大镜的功效
澳门葡京 15

在线演示
  
源码

图表放大镜

大家只怕依据 canvas
绘制一些图形也许图像,要是七个要素的坐标离得比较近,就会给成分的取舍带来一些震慑,比如大家画两条线,一个线的坐标是(200.5, 400) -> (200.5, 200),另三个线的坐标为
(201.5, 400) -> (201.5, 20),那么那两条线大概就会重叠在联合,如下图所示:
澳门葡京 16

运用图表放大镜的功能
澳门葡京 17

在线演示
  
源码

this.cObj.beginPath();
this.cObj.lineWidth = 1;
this.cObj.arc(200,200,100,Math.PI*0.75,Math.PI*2.25,false); // 绘制内层圆弧
this.cObj.strokeStyle = '#0078b4';
this.cObj.stroke();
澳门葡京 ,图片绘制格局

边框式绘制:

context.strokeStyle = "red";
context.lineWidth = 2;
context.rect(150,150,200,100);
context.stroke();

澳门葡京 18

填充式绘制:

context.fillStyle = 'yellow';
context.rect(150,150,200,100);
context.fill();

澳门葡京 19

原理

恍如于地图中的图例,放大镜使用比较标准的图例,如下图所示:
澳门葡京 20

在放大镜坐标连串中,原始的区域会变大,如下图所示
澳门葡京 21

原理

接近于地图中的图例,放大镜使用较为标准的图例,如下图所示:
澳门葡京 22

在放大镜坐标体系中,原始的区域会变大,如下图所示
澳门葡京 23

(叁)绘制外侧圆弧

填充+描边同时使用注意:

那五个效益还要选拔时,先后顺序的差异会导致效果的出入

诚如是在填充后再去描边,如下效果

context.strokeStyle = "red";
context.lineWidth = 2;
context.rect(150,150,200,100);
context.fill();
context.stroke();

澳门葡京 24

要是是描边后再填写的话,填充效果会遮挡一半的边框

//线宽被遮挡一半
context.strokeStyle = "red";
context.lineWidth = 2;
context.rect(150,150,200,100);
context.stroke();
context.fill();

澳门葡京 25

绘制原始线段

第1创造3个线条对象

function Line(xStart, yStart, xEnd, yEnd, index, color) {
    // 起点x坐标
    this.xStart = xStart;
    // 起点y坐标
    this.yStart = yStart;
    // 终点x坐标
    this.xEnd = xEnd;
    // 终点y坐标
    this.yEnd = yEnd;
    // 用来标记是哪条线段
    this.index = index;
    // 线段颜色
    this.color = color;
}

开端化线段

// 原始线段
var chartLines = new Array();
// 处于放大镜中的原始线段
var glassLines;
// 放大后的线段
var scaleGlassLines;
// 位于放大镜中的线段数量
var glassLineSize;

function initLines() {

    var line;
    line = new Line(200.5, 400, 200.5, 200, 0, "#888");
    chartLines.push(line);
    line = new Line(201.5, 400, 201.5, 20, 1, "#888");
    chartLines.push(line);


    glassLineSize = chartLines.length;
    glassLines = new Array(glassLineSize);
    for (var i = 0; i < glassLineSize; i++) {
        line = new Line(0, 0, 0, 0, i);
        glassLines[i] = line;
    }

    scaleGlassLines = new Array(glassLineSize);
    for (var i = 0; i < glassLineSize; i++) {
        line = new Line(0, 0, 0, 0, i);
        scaleGlassLines[i] = line;
    }
}

绘图线段

function drawLines() {
    var line;
    context.lineWidth = 1;

    for (var i = 0; i < chartLines.length; i++) {
        line = chartLines[i];
        context.beginPath();
        context.strokeStyle = line.color;
        context.moveTo(line.xStart, line.yStart);
        context.lineTo(line.xEnd, line.yEnd);
        context.stroke();
    }
}

绘制原始线段

率先创设1个线条对象

function Line(xStart, yStart, xEnd, yEnd, index, color) {
    // 起点x坐标
    this.xStart = xStart;
    // 起点y坐标
    this.yStart = yStart;
    // 终点x坐标
    this.xEnd = xEnd;
    // 终点y坐标
    this.yEnd = yEnd;
    // 用来标记是哪条线段
    this.index = index;
    // 线段颜色
    this.color = color;
}

开始化线段

// 原始线段
var chartLines = new Array();
// 处于放大镜中的原始线段
var glassLines;
// 放大后的线段
var scaleGlassLines;
// 位于放大镜中的线段数量
var glassLineSize;

function initLines() {

    var line;
    line = new Line(200.5, 400, 200.5, 200, 0, "#888");
    chartLines.push(line);
    line = new Line(201.5, 400, 201.5, 20, 1, "#888");
    chartLines.push(line);


    glassLineSize = chartLines.length;
    glassLines = new Array(glassLineSize);
    for (var i = 0; i < glassLineSize; i++) {
        line = new Line(0, 0, 0, 0, i);
        glassLines[i] = line;
    }

    scaleGlassLines = new Array(glassLineSize);
    for (var i = 0; i < glassLineSize; i++) {
        line = new Line(0, 0, 0, 0, i);
        scaleGlassLines[i] = line;
    }
}

绘图线段

function drawLines() {
    var line;
    context.lineWidth = 1;

    for (var i = 0; i < chartLines.length; i++) {
        line = chartLines[i];
        context.beginPath();
        context.strokeStyle = line.color;
        context.moveTo(line.xStart, line.yStart);
        context.lineTo(line.xEnd, line.yEnd);
        context.stroke();
    }
}
this.cObj.beginPath();
this.cObj.arc(200,200,120,Math.PI*0.75,Math.PI*2.25,false); // 绘制外侧圆弧
this.cObj.strokeStyle = '#c0c0c0';
this.cObj.lineCap = "round";
this.cObj.lineWidth = 20;
this.cObj.stroke();
绘制弧形

常用方法:context.arc(x,y,r,θ1,θ2,boolean)

  1. x:中心点x坐标
  2. y:中心点y坐标
  3. r:圆半径
  4. θ一:起头角度
  5. θ2:截至角度
  6. 布尔值:false逆时针(默认),true顺时针

//画弧线
context.strokeStyle = "red";
context.lineWidth = 2;
context.arc(300,100,100,0,Math.PI/2,true)
context.stroke();

澳门葡京 26

//画圆
context.beginPath();
context.arc(300,200,100,0,Math.PI*2,true)
context.stroke();

澳门葡京 27

总括原始区域和放大镜区域

function calGlassRectangle(point) {
    originalRectangle.x = point.x - originalRadius;
    originalRectangle.y = point.y - originalRadius;
    originalRectangle.width = originalRadius * 2;
    originalRectangle.height = originalRadius * 2;

    scaleGlassRectangle.width = originalRectangle.width * scale;
    scaleGlassRectangle.height = originalRectangle.height * scale;
    scaleGlassRectangle.x = originalRectangle.x + originalRectangle.width / 2 - scaleGlassRectangle.width / 2;
    scaleGlassRectangle.y = originalRectangle.y + originalRectangle.height / 2 - scaleGlassRectangle.height / 2;

    // 将值装换为整数
    scaleGlassRectangle.width = parseInt(scaleGlassRectangle.width);
    scaleGlassRectangle.height = parseInt(scaleGlassRectangle.height);
    scaleGlassRectangle.x = parseInt(scaleGlassRectangle.x);
    scaleGlassRectangle.y = parseInt(scaleGlassRectangle.y);
}

测算原始区域和放大镜区域

function calGlassRectangle(point) {
    originalRectangle.x = point.x - originalRadius;
    originalRectangle.y = point.y - originalRadius;
    originalRectangle.width = originalRadius * 2;
    originalRectangle.height = originalRadius * 2;

    scaleGlassRectangle.width = originalRectangle.width * scale;
    scaleGlassRectangle.height = originalRectangle.height * scale;
    scaleGlassRectangle.x = originalRectangle.x + originalRectangle.width / 2 - scaleGlassRectangle.width / 2;
    scaleGlassRectangle.y = originalRectangle.y + originalRectangle.height / 2 - scaleGlassRectangle.height / 2;

    // 将值装换为整数
    scaleGlassRectangle.width = parseInt(scaleGlassRectangle.width);
    scaleGlassRectangle.height = parseInt(scaleGlassRectangle.height);
    scaleGlassRectangle.x = parseInt(scaleGlassRectangle.x);
    scaleGlassRectangle.y = parseInt(scaleGlassRectangle.y);
}

(四)绘制滑块

制图像和文字字

安装文字样式:context.font = 'italic size family'

  1. italic:斜体
  2. size:字体大小
  3. family:字体

安装文字内容:context.strokeText("text",x,y)

  1. “text”:汉语字内容
  2. x:起始x坐标
  3. y:起始y坐标

context.lineWidth = 1;
context.strokeStyle = "green";
context.font = "italic 150px arial"
context.strokeText("我是文字",50,200);

澳门葡京 28

总括线段在新坐标种类的地点

由原理图大家领略,放大镜中运用坐标系的图例要比原来坐标系尤其正确,比方原本坐标系使用
1:100,那么放大镜坐标系使用
1:10,因而大家需求再一次总计线段在放大镜坐标系中的地点。同时为了方便,我们将线段的原有坐标进行了转接,减去原始区域初步的x值和y值,将要原始区域左上角的点作为为(0,0)

function calScaleLines() {
    var xStart = originalRectangle.x;
    var xEnd = originalRectangle.x + originalRectangle.width;
    var yStart = originalRectangle.y;
    var yEnd = originalRectangle.y + originalRectangle.height;
    var line, gLine, sgLine;
    var glassLineIndex = 0;
    for (var i = 0; i < chartLines.length; i++) {
        line = chartLines[i];

        // 判断线段是否在放大镜中
        if (line.xStart < xStart || line.xEnd > xEnd) {
            continue;
        }
        if (line.yEnd > yEnd || line.yStart < yStart) {
            continue;
        }

        gLine = glassLines[glassLineIndex];
        sgLine = scaleGlassLines[glassLineIndex];
        if (line.yEnd > yEnd) {
            gLine.yEnd = yEnd;
        }
        if (line.yStart < yStart) {
            gLine.yStart = yStart;
        }

        gLine.xStart = line.xStart - xStart;
        gLine.yStart = line.yStart - yStart;
        gLine.xEnd = line.xEnd - xStart;
        gLine.yEnd = line.yEnd - yStart;

        sgLine.xStart = parseInt(gLine.xStart * scale);
        sgLine.yStart = parseInt(gLine.yStart * scale);
        sgLine.xEnd = parseInt(gLine.xEnd * scale);
        sgLine.yEnd = parseInt(gLine.yEnd * scale);
        sgLine.color = line.color;
        glassLineIndex++;
    }
    glassLineSize = glassLineIndex;
}

测算线段在新坐标体系的职位

由原理图我们明白,放大镜中运用坐标系的图例要比原来坐标系更加纯粹,比如原本坐标系使用
1:100,那么放大镜坐标系使用
1:10,由此大家须要再一次总括线段在放大镜坐标系中的地方。同时为了方便,大家将线段的本来面目坐标举办了转会,减去原始区域开头的x值和y值,就要原始区域左上角的点作为为(0,0)

function calScaleLines() {
    var xStart = originalRectangle.x;
    var xEnd = originalRectangle.x + originalRectangle.width;
    var yStart = originalRectangle.y;
    var yEnd = originalRectangle.y + originalRectangle.height;
    var line, gLine, sgLine;
    var glassLineIndex = 0;
    for (var i = 0; i < chartLines.length; i++) {
        line = chartLines[i];

        // 判断线段是否在放大镜中
        if (line.xStart < xStart || line.xEnd > xEnd) {
            continue;
        }
        if (line.yEnd > yEnd || line.yStart < yStart) {
            continue;
        }

        gLine = glassLines[glassLineIndex];
        sgLine = scaleGlassLines[glassLineIndex];
        if (line.yEnd > yEnd) {
            gLine.yEnd = yEnd;
        }
        if (line.yStart < yStart) {
            gLine.yStart = yStart;
        }

        gLine.xStart = line.xStart - xStart;
        gLine.yStart = line.yStart - yStart;
        gLine.xEnd = line.xEnd - xStart;
        gLine.yEnd = line.yEnd - yStart;

        sgLine.xStart = parseInt(gLine.xStart * scale);
        sgLine.yStart = parseInt(gLine.yStart * scale);
        sgLine.xEnd = parseInt(gLine.xEnd * scale);
        sgLine.yEnd = parseInt(gLine.yEnd * scale);
        sgLine.color = line.color;
        glassLineIndex++;
    }
    glassLineSize = glassLineIndex;
}

鉴于滑块是能够运动的那里滑块的任务运用了坐标参数xy,及滑块半径r作为可变参数

潜移默化样式

线性渐变:createLinearGradient(x一,y一,x二,y二)

  1. x1/y壹:渐变开端地方坐标
  2. x2/y贰:渐变利落地点坐标

向阳渐变:createRadiusGradient(x壹,y1,r一,x二,y贰,r二)

  1. x1/y一:渐变开始圆的圆心地点坐标
  2. r一:渐变初始圆半径
  3. x2/y2:渐变利落圆的圆心地点坐标
  4. r二:渐变利落圆半径

潜移默化颜色设置:addColorStop(%,”color”);

  1. %:渐变进程,用[0,1]里头的数字代表
  2. “color”:各类渐变阶段的颜料

var linearGradient = context.createLinearGradient(100,300,800,500);

linearGradient.addColorStop(0,"red");
linearGradient.addColorStop(0.4,"yellow");
linearGradient.addColorStop(0.8,"green");
linearGradient.addColorStop(1,"blue");

context.fillStyle = linearGradient;

context.font = "italic 150px arial"
context.fillText("我是文字",50,200);

澳门葡京 29

绘图放大镜中央点

绘图放大镜中央的瞄准器

function drawAnchor() {
    context.beginPath();
    context.lineWidth = 2;
    context.fillStyle = "#fff";
    context.strokeStyle = "#000";
    context.arc(parseInt(centerPoint.x), parseInt(centerPoint.y), 10, 0, Math.PI * 2, false);

    var radius = 15;
    context.moveTo(parseInt(centerPoint.x - radius), parseInt(centerPoint.y));
    context.lineTo(parseInt(centerPoint.x + radius), parseInt(centerPoint.y));
    context.moveTo(parseInt(centerPoint.x), parseInt(centerPoint.y - radius));
    context.lineTo(parseInt(centerPoint.x), parseInt(centerPoint.y + radius));
    //context.fill();
    context.stroke();
}

制图放大镜主旨点

绘制放大镜中央的瞄准器

function drawAnchor() {
    context.beginPath();
    context.lineWidth = 2;
    context.fillStyle = "#fff";
    context.strokeStyle = "#000";
    context.arc(parseInt(centerPoint.x), parseInt(centerPoint.y), 10, 0, Math.PI * 2, false);

    var radius = 15;
    context.moveTo(parseInt(centerPoint.x - radius), parseInt(centerPoint.y));
    context.lineTo(parseInt(centerPoint.x + radius), parseInt(centerPoint.y));
    context.moveTo(parseInt(centerPoint.x), parseInt(centerPoint.y - radius));
    context.lineTo(parseInt(centerPoint.x), parseInt(centerPoint.y + radius));
    //context.fill();
    context.stroke();
}
this.cObj.beginPath();
this.cObj.moveTo(200,200);
this.cObj.arc(x,y,r,0,Math.PI*2,false); // 绘制滑块
this.cObj.fillStyle='#f15a4a';
this.cObj.fill();

this.cObj.beginPath();
this.cObj.moveTo(200,200);
this.cObj.arc(x,y,11,0,Math.PI*2,false); // 绘制滑块内侧白色区域
this.cObj.fillStyle='#ffffff';
this.cObj.fill();
制图阴影

黑影颜色:shadowColor = “color”;
黑影模糊度:shadowblur = number;
阴影偏移X:shadowOffsetX = number;
影子偏移Y:shadowOffsetY = number;

context.shadowColor = "black";
context.shadowBlur = 20;
context.shadowOffsetX = 10;
context.shadowOffsetY = 5;

context.strokeRect(100,100,100,100);

澳门葡京 30

绘制放大镜

function drawMagnifyingGlass() {

    calScaleLines();

    context.save();
    context.beginPath();
    context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false);
    context.clip();

    context.beginPath();
    context.fillStyle = "#fff";
    context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false);
    context.fill();

    context.lineWidth = 4;
    for (var i = 0; i < glassLineSize; i++) {
        context.beginPath();
        context.strokeStyle = scaleGlassLines[i].color;
        context.moveTo(scaleGlassRectangle.x + scaleGlassLines[i].xStart, scaleGlassRectangle.y + scaleGlassLines[i].yStart);
        context.lineTo(scaleGlassRectangle.x + scaleGlassLines[i].xEnd, scaleGlassRectangle.y + scaleGlassLines[i].yEnd);
        context.stroke();
    }
    context.restore();

    context.beginPath();
    var gradient = context.createRadialGradient(
        parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius - 5,
        parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius);

    gradient.addColorStop(0.50, 'silver');
    gradient.addColorStop(0.90, 'silver');
    gradient.addColorStop(1, 'black');
    context.strokeStyle = gradient;
    context.lineWidth = 5;
    context.arc(parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius, 0, Math.PI * 2, false);
    context.stroke();

    drawAnchor();
}

绘图放大镜

function drawMagnifyingGlass() {

    calScaleLines();

    context.save();
    context.beginPath();
    context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false);
    context.clip();

    context.beginPath();
    context.fillStyle = "#fff";
    context.arc(centerPoint.x, centerPoint.y, originalRadius, 0, Math.PI * 2, false);
    context.fill();

    context.lineWidth = 4;
    for (var i = 0; i < glassLineSize; i++) {
        context.beginPath();
        context.strokeStyle = scaleGlassLines[i].color;
        context.moveTo(scaleGlassRectangle.x + scaleGlassLines[i].xStart, scaleGlassRectangle.y + scaleGlassLines[i].yStart);
        context.lineTo(scaleGlassRectangle.x + scaleGlassLines[i].xEnd, scaleGlassRectangle.y + scaleGlassLines[i].yEnd);
        context.stroke();
    }
    context.restore();

    context.beginPath();
    var gradient = context.createRadialGradient(
        parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius - 5,
        parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius);

    gradient.addColorStop(0.50, 'silver');
    gradient.addColorStop(0.90, 'silver');
    gradient.addColorStop(1, 'black');
    context.strokeStyle = gradient;
    context.lineWidth = 5;
    context.arc(parseInt(centerPoint.x), parseInt(centerPoint.y), originalRadius, 0, Math.PI * 2, false);
    context.stroke();

    drawAnchor();
}

(5)绘制长度可变弧(蓝绿部分):

坐标转变

累加事变

增添事件

是因为长度可变,那里把闭合弧度作为可变参数

连锁例子能够看自个儿的另1篇博客canvas原子钟效果

鼠标拖动

鼠标移动到放大镜上,然后按下鼠标左键,可以拖动放大镜,不按鼠标左键只怕不在放大镜区域都不得以拖动放大镜。
为了促成地点的作用,大家要落成叁种事件 mousedown, mousemove,
‘mouseup’,
当鼠标按下时,检查评定是不是在放大镜区域,假设在,设置放大镜能够移动。鼠标移动时更新放大镜Samsung点的坐标。鼠标放手时,设置放大镜不得以被移位。

canvas.onmousedown = function (e) {
    var point = windowToCanvas(e.clientX, e.clientY);
    var x1, x2, y1, y2, dis;

    x1 = point.x;
    y1 = point.y;
    x2 = centerPoint.x;
    y2 = centerPoint.y;
    dis = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2);
    if (dis < Math.pow(originalRadius, 2)) {
        lastPoint.x = point.x;
        lastPoint.y = point.y;
        moveGlass = true;
    }
}

canvas.onmousemove = function (e) {
    if (moveGlass) {
        var xDis, yDis;
        var point = windowToCanvas(e.clientX, e.clientY);
        xDis = point.x - lastPoint.x;
        yDis = point.y - lastPoint.y;
        centerPoint.x += xDis;
        centerPoint.y += yDis;
        lastPoint.x = point.x;
        lastPoint.y = point.y;
        draw();
    }
}

canvas.onmouseup = function (e) {
    moveGlass = false;
}

鼠标拖动

鼠标移动到放大镜上,然后按下鼠标左键,能够拖动放大镜,不按鼠标左键也许不在放大镜区域都不得以拖动放大镜。
为了贯彻地点的作用,大家要得以达成三种事件 mousedown, mousemove,
‘mouseup’,
当鼠标按下时,检查评定是还是不是在放大镜区域,倘使在,设置放大镜可以运动。鼠标移动时更新放大镜HTC点的坐标。鼠标放手时,设置放大镜不得以被活动。

canvas.onmousedown = function (e) {
    var point = windowToCanvas(e.clientX, e.clientY);
    var x1, x2, y1, y2, dis;

    x1 = point.x;
    y1 = point.y;
    x2 = centerPoint.x;
    y2 = centerPoint.y;
    dis = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2);
    if (dis < Math.pow(originalRadius, 2)) {
        lastPoint.x = point.x;
        lastPoint.y = point.y;
        moveGlass = true;
    }
}

canvas.onmousemove = function (e) {
    if (moveGlass) {
        var xDis, yDis;
        var point = windowToCanvas(e.clientX, e.clientY);
        xDis = point.x - lastPoint.x;
        yDis = point.y - lastPoint.y;
        centerPoint.x += xDis;
        centerPoint.y += yDis;
        lastPoint.x = point.x;
        lastPoint.y = point.y;
        draw();
    }
}

canvas.onmouseup = function (e) {
    moveGlass = false;
}
this.cObj.beginPath();
this.cObj.arc(200,200,120,Math.PI*0.75,this.j,false); // 可变圆弧
this.cObj.strokeStyle = '#f15a4a';
this.cObj.lineCap = "round";
this.cObj.lineWidth = 20;
this.cObj.stroke();
坐标系平移:translate(x,y)
  1. 正是将坐标系的00点转换来(x,y)地方

鼠标双击

当移动到对应的线条上时,鼠标双击能够选拔该线段,将该线段的颜料变为灰湖绿。

canvas.ondblclick = function (e) {
    var xStart, xEnd, yStart, yEnd;
    var clickPoint = {};
    clickPoint.x = scaleGlassRectangle.x + scaleGlassRectangle.width / 2;
    clickPoint.y = scaleGlassRectangle.y + scaleGlassRectangle.height / 2;
    var index = -1;

    for (var i = 0; i < scaleGlassLines.length; i++) {
        var scaleLine = scaleGlassLines[i];

        xStart = scaleGlassRectangle.x + scaleLine.xStart - 3;
        xEnd = scaleGlassRectangle.x + scaleLine.xStart + 3;
        yStart = scaleGlassRectangle.y + scaleLine.yStart;
        yEnd = scaleGlassRectangle.y + scaleLine.yEnd;

        if (clickPoint.x > xStart && clickPoint.x < xEnd && clickPoint.y < yStart && clickPoint.y > yEnd) {
            scaleLine.color = "#f00";
            index = scaleLine.index;
            break;
        }
    }

    for (var i = 0; i < chartLines.length; i++) {
        var line = chartLines[i];
        if (line.index == index) {
            line.color = "#f00";
        } else {
            line.color = "#888";
        }
    }

    draw();
}

鼠标双击

当移动到对应的线条上时,鼠标双击能够挑选该线段,将该线段的颜色变为黄铜色。

canvas.ondblclick = function (e) {
    var xStart, xEnd, yStart, yEnd;
    var clickPoint = {};
    clickPoint.x = scaleGlassRectangle.x + scaleGlassRectangle.width / 2;
    clickPoint.y = scaleGlassRectangle.y + scaleGlassRectangle.height / 2;
    var index = -1;

    for (var i = 0; i < scaleGlassLines.length; i++) {
        var scaleLine = scaleGlassLines[i];

        xStart = scaleGlassRectangle.x + scaleLine.xStart - 3;
        xEnd = scaleGlassRectangle.x + scaleLine.xStart + 3;
        yStart = scaleGlassRectangle.y + scaleLine.yStart;
        yEnd = scaleGlassRectangle.y + scaleLine.yEnd;

        if (clickPoint.x > xStart && clickPoint.x < xEnd && clickPoint.y < yStart && clickPoint.y > yEnd) {
            scaleLine.color = "#f00";
            index = scaleLine.index;
            break;
        }
    }

    for (var i = 0; i < chartLines.length; i++) {
        var line = chartLines[i];
        if (line.index == index) {
            line.color = "#f00";
        } else {
            line.color = "#888";
        }
    }

    draw();
}

至此绘图方法成功,调用drow方法并传播参数滑块坐标、半径和拖动弧度(x,y,r,j)就能够成功图片的绘图。

坐标系旋转:rotate(θ)
  1. 将坐标系旋转θ度
  2. θ > 0时顺时针旋转,θ < 0时逆时针转动

键盘事件

因为线段离得相比近,所以选择鼠标移动很难准确的当选线段,那里运用键盘的w,
a, s, d 来进展标准移动

document.onkeyup = function (e) {
    if (e.key == 'w') {
        centerPoint.y = intAdd(centerPoint.y, -0.2);
    }
    if (e.key == 'a') {
        centerPoint.x = intAdd(centerPoint.x, -0.2);
    }
    if (e.key == 's') {
        centerPoint.y = intAdd(centerPoint.y, 0.2);
    }
    if (e.key == 'd') {
        centerPoint.x = intAdd(centerPoint.x, 0.2);
    }
    draw();
}

** 参考资料 **
HTML5-MagnifyingGlass

键盘事件

因为线段离得相比近,所以利用鼠标移动很难准确的当选线段,这里运用键盘的w,
a, s, d 来进展准确移动

document.onkeyup = function (e) {
    if (e.key == 'w') {
        centerPoint.y = intAdd(centerPoint.y, -0.2);
    }
    if (e.key == 'a') {
        centerPoint.x = intAdd(centerPoint.x, -0.2);
    }
    if (e.key == 's') {
        centerPoint.y = intAdd(centerPoint.y, 0.2);
    }
    if (e.key == 'd') {
        centerPoint.x = intAdd(centerPoint.x, 0.2);
    }
    draw();
}

** 参考资料 **
HTML5-MagnifyingGlass

4、绘图方法分析

坐标系缩放:scale(x,y)
  1. 将坐标系x方向放大x倍
  2. 将坐标系y方向放大y倍

留神:因为坐标系在通过调换之后,之后再也绘制的图样都会以这一个新的坐标系为参照;所以在绘制的时候会要命的麻烦

竭泽而渔方案如下:

save():保存当前坐标系

restore():读取保存的坐标系

  1. 每回在拓展坐标系转换以前,先使用save()将原本坐标系保存下去
  2. 在拓展转移之后,再采用restore()将坐标系恢复生机为始发坐标系
  3. 每个restore()对应相应的贰个save(),所以在全局中保留贰回随后Infinitirestore()的做法是不行的

(1)这里首先创设以canvas左上角为原点显示器坐标系,后边的绘图都将依照该坐标系,坐标图像如下:

制图贝塞尔曲线/一遍曲线

澳门葡京 31

有关例子能够看自个儿另壹篇博客canvas随拖动变化的贝塞尔曲线

编写获取当前光标地点点相对canvas坐标系(lx,ly)的章程:即当前坐标点减去canvas偏移距离

先用moveTo(x,y)设置起头点坐标
getx:function(ev){ //获取鼠标在canvas内坐标x
 return ev.clientX-this.obj.getBoundingClientRect().left;
 },
 gety:function(ev){ //获取鼠标在canvas内坐标y
 return ev.clientY-this.obj.getBoundingClientRect().top;
 }
贝塞尔曲线:bezierCurveTo(cpx1,cpy1,cpx二,cpy2,dx,dy)
  1. cpx1/xpy1:控制点1坐标
  2. cpx2/xpy2:控制点2坐标
  3. dx/dy:终点坐标

(二)为便宜创设圆的方程,那里创造八个以canvas宗旨为原点的坐标系,如下图,在实际上接纳draw方法绘图时使用的是浅绿灰的坐标系,在动用圆的门路处理是大家接纳浅橙的坐标系

贰次曲线quadraticCurveTo(cpx,cpy,dx,dy)
  1. cpx/xpy:调整点坐标
  2. dx/dy:终点坐标

context.beginPath();
context.moveTo(300,100);
//贝塞尔曲线
context.bezierCurveTo(200,100,300,300,200,300);
context.stroke()

//二次曲线
context.moveTo(300,300);
context.quadraticCurveTo(400,150,500,300);
context.stroke();

澳门葡京 32

澳门葡京 33

上面加多坐标转化方法,

显示屏坐标(石榴红坐标)->中央坐标(深橙坐标)

spotchange:function(a){ //屏幕坐标转化为中心坐标 
 var target={};
 if(a.x<200 && a.y<200){      //二象限
 target.x=-(200-a.x); 
 target.y=200-a.y; 
 }else if(a.x>200 && a.y<200){  //一象限 
 target.x=a.x-200; 
 target.y=200-a.y; 
 }else if(a.x>200 && a.y>200){  //四象限
 target.x=a.x-200;
 target.y=-(a.y-200) 
 }else if(a.x<200 && a.y>200){  //三象限
 target.x=-(200-a.x); 
 target.y=-(a.y-200); 
 } 
 return target; 
},

主干坐标(北京蓝坐标)->显示屏坐标(中绿坐标)

respotchange:function(a){ //中心坐标转化为屏幕坐标
 var target={};
 if(a.x>0 && a.y>0){
 target.x=200+a.x;
 target.y=(200-a.y);
 }else if(a.x<0 && a.y>0){
 target.x=200+a.x;
 target.y=200-a.y;
 }else if(a.x<0 && a.y<0){
 target.x=200+a.x;
 target.y=-(a.y-200)
 }else if(a.x>0 && a.y<0){
 target.x=200+a.x;
 target.y=-(a.y-200);
 }
 return target;
 },

(三)滑块路线及任务总结方法

澳门葡京 34

 **首先不思虑xy正负,**

  **测算光标地点点的正切值**

  tanφ = ly/lx;

  可知φ

  φ=arctan(tanφ)

  依靠圆的参数方程,可获取光标点对应灰绿路径地方坐标为

  x=rcosφ

  y=rsinφ

(4)**基于下面思路编写获取坐标地方方法,那里加多了xy和弧度值正负管理方法和可拖动弧度范围**

getmoveto:function(lx,ly){
 if(!this.p.isDown){ //是否可移动
 return false;
 }
 var tem={}; //存放目标坐标位置
 tem.o=Math.atan(ly/lx); //鼠标移动点圆形角
 tem.x=this.pathr*Math.cos(tem.o);
 tem.y=this.pathr*Math.sin(tem.o);
 if(lx<0){ //坐标点处理(正负)
 tem.x=-tem.x;
 tem.y=-tem.y;
 }
 if(lx>0){ //弧度值处理
 tem.z=-Math.atan(tem.y/tem.x)+Math.PI*2;
 }else{
 tem.z=-Math.atan(tem.y/tem.x)+Math.PI;
 }
 if(tem.z>7.06){ //最大值
 tem.z=7.06;
 tem.x=this.pathr*Math.cos(Math.PI*2.25);
 tem.y=-this.pathr*Math.sin(Math.PI*2.25);
 }
 if(tem.z<2.4){ //最小值
 tem.z=2.4;
 tem.x=this.pathr*Math.cos(Math.PI*0.75);
 tem.y=-this.pathr*Math.sin(Math.PI*0.75);
 }
 return tem;
 },

(五)以上措施在canvas内任性点均可视作滑块拖动的目标点,那里编写cheack方法,将限量可拖动地点限制在3个大约的环形里

check:function(x,y){ //限制可拖动范围
 var xx=x*x;
 var yy=y*y;
 var rr=114*114; //最小
 var rrr=126*126; //最大
 if(xx+yy>rr && xx+yy<rrr){
 return true;
 }
 return false;
 },

伍、事件措施编写

(一)鼠标按下推行方法OnMouseDown

此间运用了getx和gety获取光标相对canvas坐标,并认清鼠标是还是不是移动到了滑块上方地方内,(this.p是当前绘制对象,p.x即滑块横坐标,p.x即当前纵坐标,p.r即滑块最大半径),如若光标在滑块上方则设置isDown为TRUE,反正照旧,前面大家会透过isDown来推断是不是实施活动滑块的法子:

OnMouseDown:function(evt){
 var X=this.getx(evt); //获取当前鼠标位置横坐标
 var Y=this.gety(evt); //获取当前鼠标位置纵坐标
 var minX=this.p.x-this.p.r; 
 var maxX=this.p.x+this.p.r;
 var minY=this.p.y-this.p.r;
 var maxY=this.p.y+this.p.r;
 if(minX<X && X<maxX && minY<Y && Y<maxY){ //判断鼠标是否在滑块上 
  this.p.isDown=true; 
 }else{
  this.p.isDown=false;
 }
}

(二)鼠标按下后活动时滑块的章程:

OnMouseMove:function(evt){ //
 if(this.p.isDown){ //是否在滑块上按下鼠标
  var a={};  //存放当前鼠标坐标
  a.x=this.getx(evt); //坐标转化
  a.y=this.gety(evt);
  var b=this.spotchange(a); //坐标转化
  var co=this.getmoveto(b.x,b.y); //获取要移动到的坐标点
  if(this.check(b.x,b.y)){ //判断移动目标点是否在可拖动范围
  var co=this.getmoveto(b.x,b.y); //获取到移动的目标位置坐标()
  var tar=this.respotchange(co); //坐标转化
  var o=co.z;
  this.p.draw(tar.x,tar.y,this.p.r,o); //绘图
  }
 }
 },

(叁)鼠标释放方法

OnMouseUp:function(){ //鼠标释放
 this.p.isDown=false
}, 

(肆)最终将具有办法和事件绑定

event:function(){ //事件绑定
 this.obj.addEventListener("mousedown",this.OnMouseDown.bind(this),false);
 this.obj.addEventListener("mousemove",this.OnMouseMove.bind(this),false);
 this.obj.addEventListener("mouseup",this.OnMouseUp.bind(this),false);
 },

迄今停止可拖动滑块基本方法编写成功

如上正是本文的全体内容,希望本文的内容对大家的求学大概办事能带来一定的相助,同时也愿意多多协助脚本之家!

您大概感兴趣的篇章:

  • canvas完毕环形进程条效果
  • canvas达成简易的圆环进程条效果
  • canvas绘制环形进程条
  • Android使用Canvas绘制圆形进程条效果

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website