2019年6月4日 星期二
取得Youtube及Vimeo影片的縮圖
Youtube:
各種尺寸解析度的縮圖API (XXX為影片ID):
https://img.youtube.com/vi/XXX/0.jpg
https://img.youtube.com/vi/XXX/1.jpg
https://img.youtube.com/vi/XXX/2.jpg
https://img.youtube.com/vi/XXX/3.jpg
https://img.youtube.com/vi/XXX/default.jpg
https://img.youtube.com/vi/XXX/hqdefault.jpg
https://img.youtube.com/vi/XXX/mqdefault.jpg
https://img.youtube.com/vi/XXX/sddefault.jpg
https://img.youtube.com/vi/XXX/maxresdefault.jpg
回應都為縮圖的網址
--------------------------------------------------------------------------------------------------
Vimeo:
API為 (XXX為影片ID)
http://vimeo.com/api/v2/video/XXX.json
例如:
http://vimeo.com/api/v2/video/336638047.json
回應為
{
"id": 336638047,
"title": "Machine Learning",
"description": "Commissioned piece <br />\nDirector's cut<br />\n<br />\nStarring Thi-Mai Nguyen<br />\n<br />\n<br />\n<br />\nDir: Luke White<br />\nProd: Lauren Tyson<br />\nDOP: Giuseppe Favale<br />\nEditor: Brendan Jenkins<br />\nSound & Music: Mutant Jukebox<br />\nPost: FreeFolk<br />\nArt Dir: James Hamilton",
"url": "https://vimeo.com/336638047",
"upload_date": "2019-05-16 13:33:33",
"thumbnail_small": "http://i.vimeocdn.com/video/783713149_100x75.jpg",
"thumbnail_medium": "http://i.vimeocdn.com/video/783713149_200x150.jpg",
"thumbnail_large": "http://i.vimeocdn.com/video/783713149_640.jpg",
"user_id": 581986,
"user_name": "Luke White",
"user_url": "https://vimeo.com/lukewhite",
"user_portrait_small": "http://i.vimeocdn.com/portrait/31879108_30x30",
"user_portrait_medium": "http://i.vimeocdn.com/portrait/31879108_75x75",
"user_portrait_large": "http://i.vimeocdn.com/portrait/31879108_100x100",
"user_portrait_huge": "http://i.vimeocdn.com/portrait/31879108_300x300",
"stats_number_of_likes": 403,
"stats_number_of_plays": 8057,
"stats_number_of_comments": 20,
"duration": 110,
"width": 1920,
"height": 1080,
"tags": "dance, ai, robot, artificial intelligence, choreography, experiment, machine learning, mountain, TSO, elbows, technology, computer, limits, dead, falling, woman, playing",
"embed_privacy": "anywhere"
}
其中的
thumbnail_small
thumbnail_medium
thumbnail_large
就是不同尺寸大小的縮圖網址
參考資料:
2019年6月3日 星期一
Javascript Canvas - 物理球碰撞 (含重力加速度)
Javascript Canvas - 物理球碰撞 (含重力加速度) 的分享
其中用到了二維非彈性碰撞物理公式
假設每個球的質量都一樣,
設置了牆壁和球的恢復係數 (coefficient of restitution) ,
在空白處按滑鼠可以產生出一個新的球
https://jsfiddle.net/hugogo7646/ep4x608d/
html:
<canvas></canvas>
其中用到了二維非彈性碰撞物理公式
假設每個球的質量都一樣,
設置了牆壁和球的恢復係數 (coefficient of restitution) ,
在空白處按滑鼠可以產生出一個新的球
https://jsfiddle.net/hugogo7646/ep4x608d/
html:
<canvas></canvas>
==================
CSS:
CSS:
canvas {
display: block;
width: 100%;
height: 100%;
border: 1px solid blue;
}
==================
Javascript:
Javascript:
//Class definition - Start
function Ball(x, y) {
this.x = x;
this.y = y;
this.r = 10;
this.vx = 0; //Math.random(10);
this.vy = 0; //Math.random(10);
this.ax = 0;
this.ay = g;
this.color_R = Math.round(Math.random() * 255);
this.color_G = Math.round(Math.random() * 255);
this.color_B = Math.round(Math.random() * 255);
}
Ball.prototype = {
draw: function() {
this.vx += this.ax;
this.vy += this.ay;
this.x += this.vx;
this.y += this.vy;
//if ball hit with border
if (isTopWallCollision(this)) {
this.y = this.r;
this.vy *= -1 * restitutionCoefficient_of_border
}
if (isBottomWallCollision(this)) {
this.y = canvas.height - this.r;
this.vy *= -1 * restitutionCoefficient_of_border
}
if (isLeftWallCollision(this)) {
this.x = this.r;
this.vx *= -1 * restitutionCoefficient_of_border
}
if (isRightWallCollision(this)) {
this.x = canvas.width - this.r;
this.vx *= -1 * restitutionCoefficient_of_border
}
context.beginPath();
context.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
context.fillStyle = 'rgba(' + this.color_R + ', ' + this.color_G + ', ' + this.color_B + ', 1)';
context.fill()
}
}
//Class definition - End
//Config - Start //
var ballCount = 30;
var ballList = [];
var g = 0.1;
var restitutionCoefficient_of_border = 0.9;
var restitutionCoefficient_of_ball = 0.9;
//Config - End //
var canvas = document.querySelectorAll("canvas")[0];
var context = canvas.getContext("2d");
//Create balls
for (var i = 0; i < ballCount; i++) {
var isNewBallCollision = false;
var newBall;
do {
newBall = new Ball(Math.random() * canvas.width, Math.random() * canvas.height);
isNewBallCollision = isWallCollision(newBall);
if (isNewBallCollision){
continue;
}
for (var j = 0; j < ballList.length; j++) {
isNewBallCollision = isBallCollision(ballList[j], newBall);
if (isNewBallCollision) {
break;
}
}
} while (isNewBallCollision);
ballList.push(newBall);
}
draw();
canvas.addEventListener('click', function(e) {
var x = event.clientX;
var y = event.clientY;
var rect = this.getBoundingClientRect();
//Recalculate mouse offsets to relative offsets
x -= rect.left;
y -= rect.top;
//Also recalculate offsets of canvas is stretched
//changes coordinates by ratio
x *= this.width / this.clientWidth;
y *= this.height / this.clientHeight;
ballList.push(new Ball(x, y));
})
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < ballList.length; i++) {
ballList[i].draw();
for (var j = i + 1; j < ballList.length; j++) {
if (isBallCollision(ballList[i], ballList[j])) {
ballCollision(ballList[i], ballList[j]);
}
}
}
requestAnimationFrame(draw);
}
function isBallCollision(ball1, ball2) {
return Math.pow(ball1.x - ball2.x, 2) + Math.pow(ball1.y - ball2.y, 2) <= Math.pow(ball1.r + ball2.r, 2)
}
//Wall Collision functions - Start
function isWallCollision(ball) {
return isTopWallCollision(ball) || isBottomWallCollision(ball) || isLeftWallCollision(ball) || isRightWallCollision(ball);
}
function isTopWallCollision(ball) {
return (ball.y - ball.r) <= 0;
}
function isBottomWallCollision(ball) {
return (ball.y + ball.r) >= canvas.height;
}
function isLeftWallCollision(ball) {
return (ball.x - ball.r) <= 0;
}
function isRightWallCollision(ball) {
return (ball.x + ball.r) >= canvas.width;
}
//Wall Collision functions - End
function ballCollision(ball1, ball2) {
var clooisionVectorLength = Math.sqrt(Math.pow((ball1.x - ball2.x), 2) + Math.pow((ball1.y - ball2.y), 2));
var collisionX = (ball1.x - ball2.x) / clooisionVectorLength;
var collisionY = (ball1.y - ball2.y) / clooisionVectorLength;
var collision_ball1_x = Math.pow(collisionX, 2) * ball1.vx + collisionX * collisionY * ball1.vy;
var collision_ball1_y = collisionX * collisionY * ball1.vx + Math.pow(collisionY, 2) * ball1.vy;
var collision_ball2_x = Math.pow(collisionX, 2) * ball2.vx + collisionX * collisionY * ball2.vy;
var collision_ball2_y = collisionX * collisionY * ball2.vx + Math.pow(collisionY, 2) * ball2.vy;
ball1.vx = collision_ball2_x + (ball1.vx - collision_ball1_x);
ball1.vy = collision_ball2_y + (ball1.vy - collision_ball1_y);
ball2.vx = collision_ball1_x + (ball2.vx - collision_ball2_x);
ball2.vy = collision_ball1_y + (ball2.vy - collision_ball2_y);
ball1.vx *= restitutionCoefficient_of_ball;
ball1.vy *= restitutionCoefficient_of_ball;
ball2.vx *= restitutionCoefficient_of_ball;
ball2.vy *= restitutionCoefficient_of_ball;
ball1.x += collisionX * (ball1.r + ball2.r - clooisionVectorLength);
ball1.y += collisionY * (ball1.r + ball2.r - clooisionVectorLength);
ball2.x += -1 * collisionX * (ball1.r + ball2.r - clooisionVectorLength);
ball2.y += -1 * collisionY * (ball1.r + ball2.r - clooisionVectorLength);
}
function Ball(x, y) {
this.x = x;
this.y = y;
this.r = 10;
this.vx = 0; //Math.random(10);
this.vy = 0; //Math.random(10);
this.ax = 0;
this.ay = g;
this.color_R = Math.round(Math.random() * 255);
this.color_G = Math.round(Math.random() * 255);
this.color_B = Math.round(Math.random() * 255);
}
Ball.prototype = {
draw: function() {
this.vx += this.ax;
this.vy += this.ay;
this.x += this.vx;
this.y += this.vy;
//if ball hit with border
if (isTopWallCollision(this)) {
this.y = this.r;
this.vy *= -1 * restitutionCoefficient_of_border
}
if (isBottomWallCollision(this)) {
this.y = canvas.height - this.r;
this.vy *= -1 * restitutionCoefficient_of_border
}
if (isLeftWallCollision(this)) {
this.x = this.r;
this.vx *= -1 * restitutionCoefficient_of_border
}
if (isRightWallCollision(this)) {
this.x = canvas.width - this.r;
this.vx *= -1 * restitutionCoefficient_of_border
}
context.beginPath();
context.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
context.fillStyle = 'rgba(' + this.color_R + ', ' + this.color_G + ', ' + this.color_B + ', 1)';
context.fill()
}
}
//Class definition - End
//Config - Start //
var ballCount = 30;
var ballList = [];
var g = 0.1;
var restitutionCoefficient_of_border = 0.9;
var restitutionCoefficient_of_ball = 0.9;
//Config - End //
var canvas = document.querySelectorAll("canvas")[0];
var context = canvas.getContext("2d");
//Create balls
for (var i = 0; i < ballCount; i++) {
var isNewBallCollision = false;
var newBall;
do {
newBall = new Ball(Math.random() * canvas.width, Math.random() * canvas.height);
isNewBallCollision = isWallCollision(newBall);
if (isNewBallCollision){
continue;
}
for (var j = 0; j < ballList.length; j++) {
isNewBallCollision = isBallCollision(ballList[j], newBall);
if (isNewBallCollision) {
break;
}
}
} while (isNewBallCollision);
ballList.push(newBall);
}
draw();
canvas.addEventListener('click', function(e) {
var x = event.clientX;
var y = event.clientY;
var rect = this.getBoundingClientRect();
//Recalculate mouse offsets to relative offsets
x -= rect.left;
y -= rect.top;
//Also recalculate offsets of canvas is stretched
//changes coordinates by ratio
x *= this.width / this.clientWidth;
y *= this.height / this.clientHeight;
ballList.push(new Ball(x, y));
})
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < ballList.length; i++) {
ballList[i].draw();
for (var j = i + 1; j < ballList.length; j++) {
if (isBallCollision(ballList[i], ballList[j])) {
ballCollision(ballList[i], ballList[j]);
}
}
}
requestAnimationFrame(draw);
}
function isBallCollision(ball1, ball2) {
return Math.pow(ball1.x - ball2.x, 2) + Math.pow(ball1.y - ball2.y, 2) <= Math.pow(ball1.r + ball2.r, 2)
}
//Wall Collision functions - Start
function isWallCollision(ball) {
return isTopWallCollision(ball) || isBottomWallCollision(ball) || isLeftWallCollision(ball) || isRightWallCollision(ball);
}
function isTopWallCollision(ball) {
return (ball.y - ball.r) <= 0;
}
function isBottomWallCollision(ball) {
return (ball.y + ball.r) >= canvas.height;
}
function isLeftWallCollision(ball) {
return (ball.x - ball.r) <= 0;
}
function isRightWallCollision(ball) {
return (ball.x + ball.r) >= canvas.width;
}
//Wall Collision functions - End
function ballCollision(ball1, ball2) {
var clooisionVectorLength = Math.sqrt(Math.pow((ball1.x - ball2.x), 2) + Math.pow((ball1.y - ball2.y), 2));
var collisionX = (ball1.x - ball2.x) / clooisionVectorLength;
var collisionY = (ball1.y - ball2.y) / clooisionVectorLength;
var collision_ball1_x = Math.pow(collisionX, 2) * ball1.vx + collisionX * collisionY * ball1.vy;
var collision_ball1_y = collisionX * collisionY * ball1.vx + Math.pow(collisionY, 2) * ball1.vy;
var collision_ball2_x = Math.pow(collisionX, 2) * ball2.vx + collisionX * collisionY * ball2.vy;
var collision_ball2_y = collisionX * collisionY * ball2.vx + Math.pow(collisionY, 2) * ball2.vy;
ball1.vx = collision_ball2_x + (ball1.vx - collision_ball1_x);
ball1.vy = collision_ball2_y + (ball1.vy - collision_ball1_y);
ball2.vx = collision_ball1_x + (ball2.vx - collision_ball2_x);
ball2.vy = collision_ball1_y + (ball2.vy - collision_ball2_y);
ball1.vx *= restitutionCoefficient_of_ball;
ball1.vy *= restitutionCoefficient_of_ball;
ball2.vx *= restitutionCoefficient_of_ball;
ball2.vy *= restitutionCoefficient_of_ball;
ball1.x += collisionX * (ball1.r + ball2.r - clooisionVectorLength);
ball1.y += collisionY * (ball1.r + ball2.r - clooisionVectorLength);
ball2.x += -1 * collisionX * (ball1.r + ball2.r - clooisionVectorLength);
ball2.y += -1 * collisionY * (ball1.r + ball2.r - clooisionVectorLength);
}
正規表示法的 Positive/Negative Lookhead 和 Positive/Negative Lookbehind
Positive Lookhead :
a(?=b)
後面跟著 "b" 才match "a" ("b"不在match的結果裡)
例如:
可以match:ab, abc, bab
不會match:ac, ade, bad
Negative Lookhead :
a(?!b)
後面不跟著 "b" 才match "a" ("b"不在match的結果裡)
例如:
可以match:ac, ade, bad
不會match:ab, abc, bab
Positive Lookbehind:
(?<=a)b
前面跟著 "a" 才match "b" ("a"不在match的結果裡)
例如:
可以match:ab, abc, bab
不會match:xb, xbc, xbb
Negative Lookbehind:
(?<!a)b
前面不跟著 "a" 才match "b" ("a"不在match的結果裡)
例如:
可以match:xb, xbc, xbb
不會match:ab, abc, bab
應用範例:
找出單獨的 "a",不要match連續的 "a" (ex: aaa )
(?<!a)a(?!a)
解釋:
前面不跟著 "a", 才 match a(?!a),
後面不跟著 "a", 才 match a
Note:
以下網站不支援 Positive/Negative Lookbehind
Regexper
DebuggexBeta
可以用 regular expressions 101 網站測試。
Javascript, Java 等程式語言也有支援 Positive/Negative Lookbehind
參考資料:
a(?=b)
後面跟著 "b" 才match "a" ("b"不在match的結果裡)
例如:
可以match:ab, abc, bab
不會match:ac, ade, bad
Negative Lookhead :
a(?!b)
後面不跟著 "b" 才match "a" ("b"不在match的結果裡)
例如:
可以match:ac, ade, bad
不會match:ab, abc, bab
Positive Lookbehind:
(?<=a)b
前面跟著 "a" 才match "b" ("a"不在match的結果裡)
例如:
可以match:ab, abc, bab
不會match:xb, xbc, xbb
Negative Lookbehind:
(?<!a)b
前面不跟著 "a" 才match "b" ("a"不在match的結果裡)
例如:
可以match:xb, xbc, xbb
不會match:ab, abc, bab
應用範例:
找出單獨的 "a",不要match連續的 "a" (ex: aaa )
(?<!a)a(?!a)
解釋:
前面不跟著 "a", 才 match a(?!a),
後面不跟著 "a", 才 match a
Note:
以下網站不支援 Positive/Negative Lookbehind
Regexper
DebuggexBeta
可以用 regular expressions 101 網站測試。
Javascript, Java 等程式語言也有支援 Positive/Negative Lookbehind
參考資料:
訂閱:
文章
(
Atom
)