一坨很杂的东西,涉及这两三天玩耍折腾学校提供的个人主页服务的各种
SVG 是基于 XML 的被 W3C 所推荐的标准。一般情况下现代浏览器的 DOM 都会带有 SVG 的支持。在新版本的 Safari, Chrome, Firefox, IE 等浏览器中,我们不仅可以在 <canvas>
环境下利用 JavaScript 绘制位图图形,还可以在页面中创建或修改 <svg>
元素,并使用 SVG 语言绘制矢量图。
jQuery.animate()
是一个 JavaScript 动画引擎,利用它可以用于在浏览器的各个元素中实现动画。velocity.js 也是一个(可以和 jQuery 共同使用的)功能与 jQuery.animate()
相同的 JavaScript 动画引擎。它的效率表现要略高于 jQuery.animate()
(可以参考 velocity.js 官网上的对比)。velocity.js 是支持在 <svg>
元素中实现动画的,使用方法和对其它元素的操作无异。
下面这个例子是用作主页欢迎信息的动画,其中通过 describeArc
函数将圆弧计算称为贝塞尔曲线,添加到网页的 <svg>
元素中之后再利用 $.velocity()
的 rotateZ
创建动画。唯一要注意的是要在创建动画之前将贝塞尔曲线的变换中心 transform-origin
更改到圆心。
var arcNumber = 15,
picRadius = 110,
centerX = 150,
centerY = 150;
var svg = document.getElementById('onion');
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
}
function describeArc(x, y, radius, startAngle, endAngle) {
var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);
var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M", start.x, start.y,
"A", radius, radius, 0, arcSweep, 0, end.x, end.y
].join(" ");
return d;
}
for (i = 0; i < arcNumber; i++) {
var rotateDuration = 1000 + 4000 * Math.random(),
arcAngle = 180 * Math.random();
var arc = document.createElementNS("http://www.w3.org/2000/svg", 'path');
var arcMirror = document.createElementNS("http://www.w3.org/2000/svg", 'path');
arc.setAttribute("d", describeArc(centerX, centerY,
picRadius * (i + 1) / arcNumber,
0, arcAngle));
arc.setAttribute("id", 'arc' + String(i));
arc.setAttribute("transform-origin", '150 150');
arcMirror.setAttribute("d", describeArc(centerX, centerY,
picRadius * (i + 1) / arcNumber,
180, arcAngle + 180));
arcMirror.setAttribute("id", 'arcMirror' + String(i));
arcMirror.setAttribute("transform-origin", '150 150');
svg.appendChild(arc);
svg.appendChild(arcMirror);
$('#arc' + String(i)).velocity({ rotateZ: '+=360' }, {
duration: rotateDuration,
easing: 'linear',
loop: true
});
$('#arcMirror' + String(i)).velocity({ rotateZ: '+=360' }, {
duration: rotateDuration,
easing: 'linear',
loop: true
});
}
泊松圆盘分布 (Poisson Disc Distribution) 是一种点的随机分布。它在随机性的基础上尽可能地维持了点与点之间距离的均匀性。这种随机分布于我们视网膜上的视细胞的分布很相似,该分布可以用于图形采样。
具体算法如下,先指定第一个随机点,这个点就是泊松圆盘分布的第一个点。之后在已经产生的点中随机选取一个,在距离它 \(R < r < 2R\) 的范围内随机产生点,直到产生的那个随机点与其它任何点的距离都 \(> R\) 就将它保留为下一个随机点;或者连续产生的随机点达到某个阈值,使我们认为在我们选取的这个点附近无法继续产生新的点,就将它该点标记以后不再选取。重复以上步骤,直到所有点都被认定为无法产生新点,泊松圆盘的算法就算完成。
var interval = 20;
var activeNodes = [],
inactiveNodes = [],
currentId = 0,
delay = 0;
var positionReference = $('#disc').position();
var SVGobject = document.getElementById('disc');
function module (vector) {
return Math.sqrt(vector.x * vector.x + vector.y * vector.y);
}
function distance (pointA, pointB) {
var deltaX = pointA.x - pointB.x;
var deltaY = pointA.y - pointB.y;
return module({ x: deltaX, y: deltaY });
}
function poissonDiscAppend (positionX, positionY) {
activeNodes.push({ id: currentId, x: positionX, y: positionY });
var node = document.createElementNS("http://www.w3.org/2000/svg", 'circle');
node.setAttribute("r", 0);
node.setAttribute("cx", positionX);
node.setAttribute("cy", positionY);
node.setAttribute("fill", '#FF0000');
node.setAttribute("id", 'node' + String(currentId));
SVGobject.appendChild(node);
$('#node' + String(currentId++)).delay(delay).velocity({ r: 1.5 }, 300);
delay += 10;
}
function poissonDiscGenerate (originX, originY, radius) {
poissonDiscAppend(originX, originY);
while (!!activeNodes[0]) {
var index = Math.floor(Math.random() * activeNodes.length),
valid = false;
var nodeData = activeNodes[index];
for (var i = 0; i < 20; i++) {
var newNode = { x: 0, y: 0 },
pass = true;
while (module(newNode) < radius ||
module(newNode) > 2 * radius) {
newNode.x = 4 * radius * Math.random() - 2 * radius;
newNode.y = 4 * radius * Math.random() - 2 * radius;
}
newNode.x += nodeData.x;
newNode.y += nodeData.y;
if (newNode.x < 0 || newNode.x > 600 ||
newNode.y < 0 || newNode.y > 400) {
pass = false;
}
for (var j = 0; j < activeNodes.length; j++) {
if (distance(activeNodes[j], newNode) < radius) {
pass = false;
}
}
for (var k = 0; k < inactiveNodes.length; k++) {
if (distance(inactiveNodes[k], newNode) < radius) {
pass = false;
}
}
if (pass) {
poissonDiscAppend(newNode.x, newNode.y);
valid = true;
break;
}
}
if (!valid) {
inactiveNodes.push((activeNodes.splice(index, 1))[0]);
$('#node' + nodeData.id).delay(delay).velocity({ fill: "#000000" }, 100);
}
}
}
$('#disc').click(function (event) {
if (!activeNodes[0] && !inactiveNodes[0]) {
poissonDiscGenerate(event.pageX - positionReference.left,
event.pageY - positionReference.top, interval);
}
});
笔者希望在自己的个人主页上显示自己 WordPress 博客上的所有文章及其链接,怎么办?这个问题可以通过 WordPress 的插件 JSON API 解决。JSON API 可以让 WordPress 站点响应一些 JSON 请求,返回相应的 JSON 对象。同时,它也支持使用 callback
参数来解决跨域访问问题。这样,我们只需要实现写好对应的 JS
函数如 processWP
,然后通过下面这样的代码:
function processWP (WPObject) {
/* everything you want to do*/
}
$('body').append($('<script>', { src: 'http://your.wordpress.homepage/\?json\=get_recent_posts\&callback\=processWP' }));
这样,当这段代码被加载后,前面一段代码会定义一个函数 processWP
用来将 WordPress 里的信息显示在当前页面上,后面一段代码则会在当前页面中插入一个 <script>
标签,通过这个标签是可以进行跨域访问的。这个 <script>
标签作为嵌入式 JavaScript 会让浏览器载入一个格式约为 precessWP(object)
的 WordPress 站点返回的文件(其中 object
是 WordPress 站点返回的具体 JSON 内容)。执行这段脚本时,WordPress 的 JSON 对象以对象字面量的形式作为参数调用函数 processWP
,实现内容嵌入。