tag:blogger.com,1999:blog-59465307047421309702024-03-06T16:20:07.273+08:00Jax 的工作紀錄除了在整理學習上的經驗,同時也能幫助其他需要的人Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.comBlogger67125tag:blogger.com,1999:blog-5946530704742130970.post-82050368741292697022023-03-14T13:23:00.000+08:002023-03-14T13:23:27.101+08:00[ThreeJS] 噴水粒子<p>要完成這個效果有三個問題要解決</p>
<ol>
<li>自然的粒子分布</li>
<li>粒子的拋物線移動</li>
<li>粒子的擴散效果</li>
</ol>
<p>我採用簡單的方式達成</p>
<ol>
<li>用亂數產生分段的粒子團</li>
<li>用二次貝茲曲線建立拋物線</li>
<li>用粒子團的放大產生擴散效果</li>
</ol>
<p>因為我用簡單的亂數分布粒子,粒子團會有立方體現象,球體分布會更好,但多個區段連起來就不明顯了。</p>
<p>二次貝茲曲線最大的困難在計算出結尾座標的位置,透過轉換到世界空間去定位出平面高度的座標,再轉換回物體空間來計算出結尾座標。</p>
<p>動畫用 AnimationMixer 去控制,只要定出每段粒子團的開始播放時間,粒子團就會接續的移動。</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmhz-JbtzAEqJ-6qOtyJPxfYN2Q4QHDkV_gZFjVFc9bEFcrJbDx5VQdxoz78iihDYQshPENwaOOPt9l1gszc4hHAPeROD2guRic9VmNd9C6wcK2oDRWVvez4kuMR38O4X6o5_9ODEwrG-j7sM13zzMzZJEfNYo-MD8qEphGb3ijXy1qC9W7qerRM1h3g/s1222/drip-1.png"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmhz-JbtzAEqJ-6qOtyJPxfYN2Q4QHDkV_gZFjVFc9bEFcrJbDx5VQdxoz78iihDYQshPENwaOOPt9l1gszc4hHAPeROD2guRic9VmNd9C6wcK2oDRWVvez4kuMR38O4X6o5_9ODEwrG-j7sM13zzMzZJEfNYo-MD8qEphGb3ijXy1qC9W7qerRM1h3g/w640-h496/drip-1.png" /></a>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXU92nJNZD5Mk7D7tUl-3p97P3G7afTqOJ2QSbPImiog9JjNqn1g-xB4wM2iN73bAAEFyabOGNbgSgtOE94YfzhJqBJpABinizzc8nQAalarGaofuFbT33THInJU2Qlm_8g1ksHH90N7lYWgFNZYdbgZZwKmat5cqr2ZfYyf1MS4tQnxRQetfNyRl3CQ/s1222/drip-2.png"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXU92nJNZD5Mk7D7tUl-3p97P3G7afTqOJ2QSbPImiog9JjNqn1g-xB4wM2iN73bAAEFyabOGNbgSgtOE94YfzhJqBJpABinizzc8nQAalarGaofuFbT33THInJU2Qlm_8g1ksHH90N7lYWgFNZYdbgZZwKmat5cqr2ZfYyf1MS4tQnxRQetfNyRl3CQ/w640-h496/drip-2.png" /></a>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsbWW51b3o786UYIVvIIMg79twNteoTLcunQJJijBfdRuMDwj_hotyen8wiCywjQFWk6TjXPiKlwGiXjpf9P3Zc_1uDts16hhRLaOhYp1yR7gBiXoDITt8PourSwnMvGY26VQXMwjrDS0SUjz_3uA4e2fO0hhJTq8XhnmIFBr2HmBCz58SyGwilNw2qQ/s1222/drip-3.png"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsbWW51b3o786UYIVvIIMg79twNteoTLcunQJJijBfdRuMDwj_hotyen8wiCywjQFWk6TjXPiKlwGiXjpf9P3Zc_1uDts16hhRLaOhYp1yR7gBiXoDITt8PourSwnMvGY26VQXMwjrDS0SUjz_3uA4e2fO0hhJTq8XhnmIFBr2HmBCz58SyGwilNw2qQ/w640-h496/drip-3.png" /></a>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLInXsCCm-PTPcBC18HGFC2-wl6MrQJXnelsql_b_X9TNM8G3Rrkj4O6Umv_A7QCEgov3BqjFEyCUZSFsBPEXzUIofL_wo5zhtyrOwS9uFPlqr13wNJ6ew-VZI6Hm3pFSePlJZs7QSjlsxFXQiMHV52a7DBw5jN8_NRG7wdWbK5NsJjLnLxebKvldgng/s1222/drip-4.png"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLInXsCCm-PTPcBC18HGFC2-wl6MrQJXnelsql_b_X9TNM8G3Rrkj4O6Umv_A7QCEgov3BqjFEyCUZSFsBPEXzUIofL_wo5zhtyrOwS9uFPlqr13wNJ6ew-VZI6Hm3pFSePlJZs7QSjlsxFXQiMHV52a7DBw5jN8_NRG7wdWbK5NsJjLnLxebKvldgng/w640-h496/drip-4.png" /></a>
<div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh8H-A3wWzAnl_LPnlxBiYfGbHG6lxTWIU-lUGyc22rCa_eiUQolcB1kOHpNXO6r5xZf0WE4kUv8n_0bG9_HXozIU6kepPJ3svSNd9MpKW3IJOOoThXtmcN31ykjGVwXeBRzd1HB3AueaM6CUHWmG3NP6KQv_IgmtjEstESKultwAjbs19ggVAsLuGTg/s1600/circle.png"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh8H-A3wWzAnl_LPnlxBiYfGbHG6lxTWIU-lUGyc22rCa_eiUQolcB1kOHpNXO6r5xZf0WE4kUv8n_0bG9_HXozIU6kepPJ3svSNd9MpKW3IJOOoThXtmcN31ykjGVwXeBRzd1HB3AueaM6CUHWmG3NP6KQv_IgmtjEstESKultwAjbs19ggVAsLuGTg/s1600/circle.png"/></a> circle.png</div>
<p>程式:</p>
<pre class="js" name="code">
/* 初始化渲染器 */
const renderer = new THREE.WebGLRenderer({ antialias: true });
document.getElementById('Container').appendChild(renderer.domElement);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
/* 初始化場景 */
const scene = new THREE.Scene();
scene.background = new THREE.Color('#111'); /* 背景顏色 */
scene.add(new THREE.AmbientLight('#FFF', 0.5)); /* 加入環境光 */
/* 地面 */
const floorMesh = new THREE.Mesh(
new THREE.PlaneGeometry(200, 200),
new THREE.MeshBasicMaterial({ color: '#DDD', depthWrite: false })
);
floorMesh.rotation.x = THREE.MathUtils.degToRad(-90);
scene.add(floorMesh);
/* 初始化鏡頭 */
const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 300);
camera.position.set(0, 10, 22);
/* 初始化軌道控制,鏡頭的移動 */
const orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
orbitControls.update();
/* 動畫刷新器 */
const mixers = [];
function newMixer(target) {
const mixer = new THREE.AnimationMixer(target);
mixers.push(mixer);
return mixer;
}
/* 動畫計時器 */
const clock = new THREE.Clock();
/* 渲染週期 */
function renderCycle() {
const delta = clock.getDelta();
mixers.forEach(x => x.update(delta));
renderer.render(scene, camera);
requestAnimationFrame(renderCycle);
}
renderCycle();
/*---------------------------------------------------------------*/
/* 水管物件,水滴粒子群組會附加在這裡 */
const pipeMesh = new THREE.Mesh(
new THREE.CylinderGeometry(0.5, 0.5, 9, 16),
new THREE.MeshBasicMaterial( {color: '#eea236'} )
);
pipeMesh.rotation.x = THREE.MathUtils.degToRad(0);
pipeMesh.position.set(0, 4, 0);
scene.add(pipeMesh);
const dripGroup = new THREE.Group();
dripGroup.position.set(0, 4, 0);
pipeMesh.add(dripGroup);
/* 水滴材質 */
const dripMaterial = new THREE.PointsMaterial({
map: new THREE.TextureLoader().load('circle.png'),
color: '#0aa',
size: 1,
opacity: 0.7,
depthWrite: false,
transparent: true,
});
/* 建立水滴,用亂數建立粒子點 */
for (let i = 0; i < 60; i++) {
const vertices = [];
for (let j = 0; j < 40; j++) {
const x = Math.random() - 0.5;
const y = Math.random() - 0.5;
const z = Math.random() - 0.5;
vertices.push(x, y, z);
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
const particles = new THREE.Points(geometry, dripMaterial);
dripGroup.add(particles);
newMixer(particles); /* 水滴動畫 */
}
/*---------------------------------------------------------------*/
const cycle = 2; /* 循環週期,會影響水流速度 */
const scale = 8; /* 擴散大小 */
const length = 14; /* 水流長度 */
/* 二次貝茲曲線,用來取得拋物線的座標點 */
const curve = new THREE.QuadraticBezierCurve3();
curve.v1.set(0, length, 0); /* 曲線控制點座標 */
/* 計算結尾座標 */
const rootPos = dripGroup.getWorldPosition(new THREE.Vector3());
const quaternion = dripGroup.getWorldQuaternion(new THREE.Quaternion());
const toPos = curve.v1.clone();
toPos.applyQuaternion(quaternion); /* 轉換到世界空間 */
/* 當水流是向上時,增加平面位置 */
if (toPos.y > (length / 3)) {
toPos.x *= 1.8;
toPos.z *= 1.8;
}
toPos.y = Math.min(-rootPos.y, toPos.y * 1.5); /* 將結尾拉回平面高度 */
toPos.applyQuaternion(quaternion.conjugate()); /* 轉換回物體空間 */
curve.v2.copy(toPos); /* 曲線結尾點座標 */
/* 建立拋物線及擴散動畫 */
const points = curve.getPoints(10); /* 曲线分段 */
const curveTime = [];
const curvePos = [];
points.forEach((v, i) => {
curveTime.push(cycle * i / points.length);
curvePos.push(v.x, v.y, v.z);
});
const posTrack = new THREE.VectorKeyframeTrack('.position', curveTime, curvePos);
const scaleTrack = new THREE.VectorKeyframeTrack('.scale', [0, cycle], [0, 0, 0, scale, scale, scale]);
const clip = new THREE.AnimationClip('scale', cycle, [posTrack, scaleTrack]);
mixers.forEach((mixer, i) => {
const action = mixer.clipAction(clip);
action.time = cycle * i / mixers.length;
action.play();
});
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-32993693243151874492023-03-07T16:55:00.002+08:002023-03-07T16:55:34.802+08:00[ThreeJS] 單次動畫<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbo9n18dLc472NzcMhlY6zHpO0QDGNzKLubchjXeMtmQ22VSPWIsZbHwIFoMiAiBvDAyyty8DIsmBBxOjB2Ukdu2LKldj-M12XmDP9sBhxx4vKqxUHaaGkfKf4TtR6MO69qzz_ZRhOwwrf-baKZ1_SyxH2Nh7qtZzE4aW_VcUjOfi6qBMCY0dsYD4tXw/s777/1678178422439.jpg"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbo9n18dLc472NzcMhlY6zHpO0QDGNzKLubchjXeMtmQ22VSPWIsZbHwIFoMiAiBvDAyyty8DIsmBBxOjB2Ukdu2LKldj-M12XmDP9sBhxx4vKqxUHaaGkfKf4TtR6MO69qzz_ZRhOwwrf-baKZ1_SyxH2Nh7qtZzE4aW_VcUjOfi6qBMCY0dsYD4tXw/s600/1678178422439.jpg"/></a>
<pre class="js" name="code">
/* 單次動畫 */
function animateOnce(mixer, prop, dur, values) {
/* 停止 & 清除 先前的動畫 */
mixer.stopAllAction();
mixer.uncacheRoot(mixer.getRoot());
/* 增加動畫片段 */
const track = new THREE.KeyframeTrack(prop, [0, dur], values);
const clip = new THREE.AnimationClip('move', dur, [track]);
const action = mixer.clipAction(clip);
action.clampWhenFinished = true;
action.setLoop(THREE.LoopOnce);
/* Promise 來處理完成事件,這樣就可以用 await */
return new Promise((resolve) => {
const finished = function () {
mixer.removeEventListener('finished', finished);
resolve();
};
mixer.addEventListener('finished', finished);
action.play(); /* 播放動畫 */
});
}
</pre>
<p>想要能像 SVG 一樣簡單的控制模型的移動,ThreeJS 並沒有直接提供我想要的模式,試了很久終於成功了。</p>
<p>mixer 中的 action 是不斷添加的,這會混亂播放動畫,所以在播放新的動畫前必須[停止]且[清除]先前的動畫。</p>
<p>為了可以簡單串接不同模型的動畫,所以就想用 await 來達成,所以加上 Promise 的包裝。</p>
<h3>完整程式</h3>
<pre class="js" name="code">
/* 初始化渲染器 */
const renderer = new THREE.WebGLRenderer({ antialias: true });
document.getElementById('Container').appendChild(renderer.domElement);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
/* 初始化場景 */
const scene = new THREE.Scene();
scene.background = new THREE.Color('#000'); /* 背景顏色 */
scene.add(new THREE.AmbientLight('#FFF', 0.5)); /* 加入環境光 */
scene.add(new THREE.AxesHelper(50)); /* 3D 軸標示 */
/* 初始化鏡頭 */
const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 300);
camera.position.set(0, 4, 12);
/* 初始化軌道控制,鏡頭的移動 */
const orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
orbitControls.update();
/* 動畫刷新器 */
const mixers = [];
function newMixer(target) {
const mixer = new THREE.AnimationMixer(target);
mixers.push(mixer);
return mixer;
}
/* 動畫計時器 */
const clock = new THREE.Clock();
/* 渲染週期 */
function renderCycle() {
const delta = clock.getDelta();
mixers.forEach(x => x.update(delta));
renderer.render(scene, camera);
requestAnimationFrame(renderCycle);
}
renderCycle();
/*---------------------------------------------------------------*/
/* 單次動畫 */
function animateOnce(mixer, prop, dur, values) {
/* 停止 & 清除 先前的動畫 */
mixer.stopAllAction();
mixer.uncacheRoot(mixer.getRoot());
/* 增加動畫片段 */
const track = new THREE.KeyframeTrack(prop, [0, dur], values);
const clip = new THREE.AnimationClip('move', dur, [track]);
const action = mixer.clipAction(clip);
action.clampWhenFinished = true;
action.setLoop(THREE.LoopOnce);
/* Promise 來處理完成事件,這樣就可以用 await */
return new Promise((resolve) => {
const finished = function () {
mixer.removeEventListener('finished', finished);
resolve();
};
mixer.addEventListener('finished', finished);
action.play(); /* 播放動畫 */
});
}
const cubeA = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial( {color: '#0F0'} )
);
const mixerA = newMixer(cubeA);
scene.add(cubeA);
const cubeB = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial( {color: '#00F'} )
);
const mixerB = newMixer(cubeB);
scene.add(cubeB);
async function run() {
await animateOnce(mixerA, '.position[x]', 3, [0, 3]);
await animateOnce(mixerB, '.position[y]', 3, [0, 2]);
await animateOnce(mixerA, '.position[x]', 3, [3, 0]);
await animateOnce(mixerB, '.position[y]', 3, [2, 0]);
}
run();
</pre>
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-52605017913973088232023-03-07T16:06:00.000+08:002023-03-07T16:06:17.401+08:00[ThreeJS] 用 SVG 貼圖顯示中文<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-eIPGHRCTfU8B7bXUFDmlG-NWXsfJWy5fyj_cxq0pgwYvO8Q_vBssL74B-KqSy5eNn4k39aX3d6Lyo7mqEepu7xtwQPw-tvDejUn0f1ytd890LFB9gb3-beeGoV4Pj9pZ6XRpdUMColA3sb2gDKnQXWwaq1kUoOGU9Lernxsuv85ScHYaCE5-GVuIag/s896/1678174842753.jpg"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-eIPGHRCTfU8B7bXUFDmlG-NWXsfJWy5fyj_cxq0pgwYvO8Q_vBssL74B-KqSy5eNn4k39aX3d6Lyo7mqEepu7xtwQPw-tvDejUn0f1ytd890LFB9gb3-beeGoV4Pj9pZ6XRpdUMColA3sb2gDKnQXWwaq1kUoOGU9Lernxsuv85ScHYaCE5-GVuIag/w498-h640/1678174842753.jpg" /></a>
<p>HTML</p>
<pre class="xml" name="code">
<div id="Container"></div>
<div id="LabelTpl" style="display:none;">
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 30 30">
<text x="15" y="15" fill="#fff" text-anchor="middle"></text>
</svg>
</div>
<script src="../Scripts/three.145/three.min.js"></script>
<script src="../Scripts/three.145/controls/OrbitControls.js"></script>
<script src="../Scripts/three.145/loaders/GLTFLoader.js"></script>
<script src="main.js"></script>
</pre>
<p>main.js</p>
<pre class="js" name="code">
/* 初始化渲染器 */
const renderer = new THREE.WebGLRenderer({ antialias: true });
document.getElementById('Container').appendChild(renderer.domElement);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
/* 初始化場景 */
const scene = new THREE.Scene();
scene.background = new THREE.Color('#000'); /* 背景顏色 */
scene.add(new THREE.AmbientLight('#FFF', 0.5)); /* 加入環境光 */
scene.add(new THREE.AxesHelper(50)); /* 3D 軸標示 */
/* 初始化鏡頭 */
const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 300);
camera.position.set(0, 4, 12);
/* 初始化軌道控制,鏡頭的移動 */
const orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
orbitControls.update();
/* 渲染週期 */
function renderCycle() {
renderer.render(scene, camera);
requestAnimationFrame(renderCycle);
}
renderCycle();
/*---------------------------------------------------------------*/
function svgBase64(svg) {
svg = svg.replace(/\s+</g, '<').replace(/>\s+/g, '>');
return 'data:image/svg+xml,' + encodeURIComponent(svg);
}
const textureLoader = new THREE.TextureLoader();
const $labelTpl = document.querySelector('#LabelTpl');
const $labelText = document.querySelector('#LabelTpl text');
/* 環線 */
function addCircle(label, z) {
$labelText.innerHTML = label;
let imageData = svgBase64($labelTpl.innerHTML);
const particles = new THREE.Points(
new THREE.EdgesGeometry(new THREE.CircleGeometry(10, 6)),
new THREE.PointsMaterial({
map: textureLoader.load(imageData),
color: '#FFF',
size: 4,
depthWrite: false,
transparent: true,
})
);
particles.position.z = z;
scene.add(particles);
const circle = new THREE.LineSegments(
new THREE.EdgesGeometry(new THREE.CircleGeometry(10, 60)),
new THREE.LineBasicMaterial({ color: '#FFF' })
);
circle.position.z = z;
scene.add(circle);
}
addCircle('冬至', 4);
addCircle('夏至', -4);
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-81763721259786817372023-03-07T13:01:00.000+08:002023-03-07T13:01:25.412+08:00[ThreeJS] 解決陰影造成的條紋<p> 會出現條紋現象是因為雙面材質,只要設置為單面材質就可以解決。</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3bYF4LujAM-qlqtF9Xbo2GLyovpbv0dF-i10fta93z9zzQHQBs4aRNqIsQNRsPpyJFe74mk2Ph-9U5en7J9iNXKagiPyc4tdK2qzf7w7D0TvPufjYHwGGD4u7YCHvTPxmeZ1T-pnVnil78yCrLf3190tSBxBMsyN7nWoIdFGm6jeKi9YVwOZWFj5V_Q/s1094/1678164814278.jpg"><img width="640" height="542" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3bYF4LujAM-qlqtF9Xbo2GLyovpbv0dF-i10fta93z9zzQHQBs4aRNqIsQNRsPpyJFe74mk2Ph-9U5en7J9iNXKagiPyc4tdK2qzf7w7D0TvPufjYHwGGD4u7YCHvTPxmeZ1T-pnVnil78yCrLf3190tSBxBMsyN7nWoIdFGm6jeKi9YVwOZWFj5V_Q/w640-h542/1678164814278.jpg" /></a>
<pre class="js" name="code">
/* 載入模型 */
new THREE.GLTFLoader().load('Model.glb', function (gltf) {
const model = gltf.scene;
model.traverse(obj => {
if (!obj.isMesh) { return; }
obj.frustumCulled = false;
obj.castShadow = true;
obj.receiveShadow = true;
/* 解決陰影造成的條紋 */
obj.material.side = THREE.FrontSide;
obj.material.shadowSide = THREE.FrontSide;
});
scene.add(model);
});
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-73905654130388785572023-03-07T11:37:00.002+08:002023-03-07T11:37:41.939+08:00[ThreeJS] 載入壓縮過 GLTF 檔<pre class="js" name="code">
/* 解壓縮 lib 載入器
* https://threejs.org/docs/#examples/en/loaders/DRACOLoader
* https://google.github.io/draco/
*/
const dracoLoader = new THREE.DRACOLoader();
/* 指定解壓器的位置 */
dracoLoader.setDecoderPath('../Scripts/three.145/libs/draco/');
/* GLTF 載入器 */
const gltfLoader = new THREE.GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
/* 載入模型 */
gltfLoader.load('Model.glb', function (gltf) {
const model = gltf.scene;
scene.add(model);
});
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-13600331491016190452022-09-05T11:29:00.000+08:002022-09-05T11:29:09.331+08:00[轉載] opencv js getPerspectiveTransform,perspectiveTransform 方法使用轉載自: <a href="https://blog.csdn.net/qq_45062700/article/details/121968913" target="_blank">opencvjs getPerspectiveTransform,perspectiveTransform方法使用</a>
<p>OpenCV JavaScript版本,使用getPerspectiveTransform,PerspectiveTransform方法。</p>
<p>JavaScript和python版本不同的是Mat的创建方法不同,python会在内部自动把数据转换成Mat类,也JavaScript不会,所以刚开始没有找到JavaScript创建Mat方法,走了很多弯路。</p>
<p>这只是测试代码,没有使用项目中真实的数据,所有有一定的偏差。</p>
<p>如果有什么错误,欢迎纠正。</p>
<p>下面上代码:</p>
<pre class="xml" name="code">
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello OpenCV.js</title>
</head>
<body>
<h2>Hello OpenCV.js</h2>
<h2>getPerspectiveTransform,PerspectiveTransform方法使用</h2>
<p id="status">OpenCV.js is loading...</p>
<div>
<button onClick="myclick()">输出结果</button>
</div>
<script type="text/javascript">
function myclick() {
//代码 getPerspectiveTransform
//创建数据
let srcTri = cv.matFromArray(4, 1, cv.CV_32FC2, [56, 65, 368, 52, 28, 387, 389, 390]);
let dstTri = cv.matFromArray(4, 1, cv.CV_32FC2, [0, 0, 300, 0, 0, 300, 300, 300]);
//转换的数据
let M = cv.getPerspectiveTransform(srcTri, dstTri);
console.log("getPerspectiveTransform M", M);
//==== PerspectiveTransform ======
//point点的数据一定要是一维的,opencv会自己去处理
let points = [
1, 2,
3, 4,
5, 6,
7, 8
];
//原数据
points = cv.matFromArray(4,1,cv.CV_32FC2,points);
//转换后的数据
let points_trans = new cv.Mat();
cv.perspectiveTransform(points, points_trans,M);
console.log("points", points);
console.log("points_trans", points_trans);
}
function onOpenCvReady() {
document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
}
</script>
<script async src="https://docs.opencv.org/4.x/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
<script src="https://docs.opencv.org/4.x/utils.js" type="text/javascript"></script>
</body>
</html>
</pre>
<p>getPerspectiveTransform结果</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYc1sL1bpnMBw9e6LeqMSuj99cRUCbdVglyqZT0ZV5lpk7_869pUwQld0JFDlKoDVWSTyy4-BSibSC4nUuwbaitKQ8TdNoSsarAK_LlpvFkyP6wTOzW3TtchA6vW2gWROJl7IWjwyq3mEm9-KxC8c3VZXqqNKu1WFrQRnqF7tCBKjSleawWFxjPbvAkg/s944/10a9bc90a9a545dc981bb237618c17c5.jpg"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYc1sL1bpnMBw9e6LeqMSuj99cRUCbdVglyqZT0ZV5lpk7_869pUwQld0JFDlKoDVWSTyy4-BSibSC4nUuwbaitKQ8TdNoSsarAK_LlpvFkyP6wTOzW3TtchA6vW2gWROJl7IWjwyq3mEm9-KxC8c3VZXqqNKu1WFrQRnqF7tCBKjSleawWFxjPbvAkg/s600/10a9bc90a9a545dc981bb237618c17c5.jpg"/></a>
<p>perspectiveTransform结果</p>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaDylEsRU5YPijz7zQjw-pv6R8WAHjOO9PckjNPO-8Pcoz82dPyy_hvWtDU5FL6h45sVBv3C6J_-OlW2_zxr1s5YGnKfZtmv054EirLUT1cxi0VayLSM3OD2nnH-HJe8kbX8RYlijCBh_66Zy7V7Vrit7V27FOzxaw-Vd8XCGMJ_xWMHsBIK0eqgEp6g/s962/a442d80f5cc840b1aa08ce27a4950dba.jpg"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaDylEsRU5YPijz7zQjw-pv6R8WAHjOO9PckjNPO-8Pcoz82dPyy_hvWtDU5FL6h45sVBv3C6J_-OlW2_zxr1s5YGnKfZtmv054EirLUT1cxi0VayLSM3OD2nnH-HJe8kbX8RYlijCBh_66Zy7V7Vrit7V27FOzxaw-Vd8XCGMJ_xWMHsBIK0eqgEp6g/s600/a442d80f5cc840b1aa08ce27a4950dba.jpg"/></a>
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-7904583457729593852022-07-15T12:38:00.000+08:002022-07-15T12:38:39.289+08:00[轉載] CSS 变量教程轉載自:<a href="http://www.ruanyifeng.com/blog/2017/05/css-variables.html" target="_blank">阮一峰 CSS 变量教程</a>
<p>今年三月,微软<a href="https://www.neowin.net/news/css-custom-properties-are-coming-to-microsoft-edge-in-the-windows-10-creators-update" target="_blank">宣布</a> Edge 浏览器将支持 CSS 变量。</p>
<p>这个重要的 CSS 新功能,所有主要浏览器已经都支持了。本文全面介绍如何使用它,你会发现原生 CSS 从此变得异常强大。</p>
<h3>一、变量的声明</h3>
<p>声明变量的时候,变量名前面要加两根连词线(<code>--</code>)。</p>
<pre class="css:nogutter:nocontrols" name="code">
body {
--foo: #7F583F;
--bar: #F7EFD2;
}
</pre>
<p>上面代码中,<code>body</code>选择器里面声明了两个变量:<code>--foo</code>和<code>--bar</code>。</p>
<p>它们与<code>color</code>、<code>font-size</code>等正式属性没有什么不同,只是没有默认含义。所以 CSS 变量(CSS variable)又叫做<strong>"CSS 自定义属性"</strong>(CSS custom properties)。因为变量与自定义的 CSS 属性其实是一回事。</p>
<p>你可能会问,为什么选择两根连词线(<code>--</code>)表示变量?因为<code>$foo</code>被 Sass 用掉了,<code>@foo</code>被 Less 用掉了。为了不产生冲突,官方的 CSS 变量就改用两根连词线了。 </p>
<p>各种值都可以放入 CSS 变量。</p>
<pre class="css:nogutter:nocontrols" name="code">
:root{
--main-color: #4d4e53;
--main-bg: rgb(255, 255, 255);
--logo-border-color: rebeccapurple;
--header-height: 68px;
--content-padding: 10px 20px;
--base-line-height: 1.428571429;
--transition-duration: .35s;
--external-link: "external link";
--margin-top: calc(2vh + 20px);
}
</pre>
<p>变量名大小写敏感,<code>--header-color</code>和<code>--Header-Color</code>是两个不同变量。</p>
<h3>二、var() 函数</h3>
<p><code>var()</code>函数用于读取变量。</p>
<pre class="css:nogutter:nocontrols" name="code">
a {
color: var(--foo);
text-decoration-color: var(--bar);
}
</pre>
<p><code>var()</code>函数还可以使用第二个参数,表示变量的默认值。如果该变量不存在,就会使用这个默认值。</p>
<pre class="css:nogutter:nocontrols" name="code">
color: var(--foo, #7F583F);
</pre>
<p>第二个参数不处理内部的逗号或空格,都视作参数的一部分。</p>
<pre class="css:nogutter:nocontrols" name="code">
var(--font-stack, "Roboto", "Helvetica");
var(--pad, 10px 15px 20px);
</pre>
<p><code>var()</code>函数还可以用在变量的声明。</p>
<pre class="css:nogutter:nocontrols" name="code">
:root {
--primary-color: red;
--logo-text: var(--primary-color);
}
</pre>
<p>注意,变量值只能用作属性值,不能用作属性名。</p>
<pre class="css:nogutter:nocontrols" name="code">
.foo {
--side: margin-top;
/* 无效 */
var(--side): 20px;
}
</pre>
<p>上面代码中,变量<code>--side</code>用作属性名,这是无效的。</p>
<h3>三、变量值的类型</h3>
<p>如果变量值是一个字符串,可以与其他字符串拼接。</p>
<pre class="css:nogutter:nocontrols" name="code">
--bar: 'hello';
--foo: var(--bar)' world';
</pre>
<p>利用这一点,可以 debug(<a href="https://codepen.io/malyw/pen/oBWMOY" target="_blank">例子</a>)。</p>
<pre class="css:nogutter:nocontrols" name="code">
body:after {
content: '--screen-category : 'var(--screen-category);
}
</pre>
<p>如果变量值是数值,不能与数值单位直接连用。</p>
<pre class="css:nogutter:nocontrols" name="code">
.foo {
--gap: 20;
/* 无效 */
margin-top: var(--gap)px;
}
</pre>
<p>上面代码中,数值与单位直接写在一起,这是无效的。必须使用<code>calc()</code>函数,将它们连接。</p>
<pre class="css:nogutter:nocontrols" name="code">
.foo {
--gap: 20;
margin-top: calc(var(--gap) * 1px);
}
</pre>
<p>如果变量值带有单位,就不能写成字符串。</p>
<pre class="css:nogutter:nocontrols" name="code">
/* 无效 */
.foo {
--foo: '20px';
font-size: var(--foo);
}
/* 有效 */
.foo {
--foo: 20px;
font-size: var(--foo);
}
</pre>
<br/>
<h3>四、作用域</h3>
<p>同一个 CSS 变量,可以在多个选择器内声明。读取的时候,优先级最高的声明生效。这与 CSS 的"层叠"(cascade)规则是一致的。</p>
<p>下面是一个<a href="http://jsbin.com/buwahixoqo/edit?html,css,output" target="_blank">例子</a>。</p>
<pre class="xml:nogutter:nocontrols" name="code">
<style>
:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
</style>
<p>蓝色</p>
<div>绿色</div>
<div id="alert">红色</div>
</pre>
<p>上面代码中,三个选择器都声明了<code>--color</code>变量。不同元素读取这个变量的时候,会采用优先级最高的规则,因此三段文字的颜色是不一样的。</p>
<p>这就是说,变量的作用域就是它所在的选择器的有效范围。</p>
<pre class="css:nogutter:nocontrols" name="code">
body {
--foo: #7F583F;
}
.content {
--bar: #F7EFD2;
}
</pre>
<p>上面代码中,变量<code>--foo</code>的作用域是<code>body</code>选择器的生效范围,<code>--bar</code>的作用域是<code>.content</code>选择器的生效范围。</p>
<p>由于这个原因,全局的变量通常放在根元素<code>:root</code>里面,确保任何选择器都可以读取它们。</p>
<pre class="css:nogutter:nocontrols" name="code">
:root {
--main-color: #06c;
}
</pre>
<br/>
<h3>五、响应式布局</h3>
<p>CSS 是动态的,页面的任何变化,都会导致采用的规则变化。</p>
<p>利用这个特点,可以在响应式布局的<code>media</code>命令里面声明变量,使得不同的屏幕宽度有不同的变量值。</p>
<pre class="css:nogutter:nocontrols" name="code">
body {
--primary: #7F583F;
--secondary: #F7EFD2;
}
a {
color: var(--primary);
text-decoration-color: var(--secondary);
}
@media screen and (min-width: 768px) {
body {
--primary: #F7EFD2;
--secondary: #7F583F;
}
}
</pre>
<br/>
<h3>六、兼容性处理</h3>
<p>对于不支持 CSS 变量的浏览器,可以采用下面的写法。</p>
<pre class="css:nogutter:nocontrols" name="code">
a {
color: #7F583F;
color: var(--primary);
}
</pre>
<p>也可以使用<code>@support</code>命令进行检测。</p>
<pre class="css:nogutter:nocontrols" name="code">
@supports ( (--a: 0)) {
/* supported */
}
@supports ( not (--a: 0)) {
/* not supported */
}
</pre>
<br/>
<h3>七、JavaScript 操作</h3>
<p>JavaScript 也可以检测浏览器是否支持 CSS 变量。</p>
<pre class="js:nogutter:nocontrols" name="code">
const isSupported =
window.CSS &&
window.CSS.supports &&
window.CSS.supports('--a', 0);
if (isSupported) {
/* supported */
} else {
/* not supported */
}
</pre>
<p>JavaScript 操作 CSS 变量的写法如下。</p>
<pre class="js:nogutter:nocontrols" name="code">
// 设置变量
document.body.style.setProperty('--primary', '#7F583F');
// 读取变量
document.body.style.getPropertyValue('--primary').trim();
// '#7F583F'
// 删除变量
document.body.style.removeProperty('--primary');
</pre>
<p>这意味着,JavaScript 可以将任意值存入样式表。下面是一个监听事件的例子,事件信息被存入 CSS 变量。</p>
<pre class="js:nogutter:nocontrols" name="code">
const docStyle = document.documentElement.style;
document.addEventListener('mousemove', (e) => {
docStyle.setProperty('--mouse-x', e.clientX);
docStyle.setProperty('--mouse-y', e.clientY);
});
</pre>
<p>那些对 CSS 无用的信息,也可以放入 CSS 变量。</p>
<pre class="css:nogutter:nocontrols" name="code">
--foo: if(x > 5) this.width = 10;
</pre>
<p>上面代码中,<code>--foo</code>的值在 CSS 里面是无效语句,但是可以被 JavaScript 读取。这意味着,可以把样式设置写在 CSS 变量中,让 JavaScript 读取。</p>
<p>所以,CSS 变量提供了 JavaScript 与 CSS 通信的一种途径。</p>
<h3>八、参考链接</h3>
<ul>
<li><a href="https://vgpena.github.io/winning-with-css-variables/" target="_blank">Winning with CSS Variables</a></li>
<li><a href="https://developers.google.com/web/updates/2016/02/css-variables-why-should-you-care" target="_blank">CSS Variables: Why Should You Care?</a></li>
<li><a href="https://www.smashingmagazine.com/2017/04/start-using-css-custom-properties/" target="_blank">It's Time To Start Using CSS Custom Properties</a></li>
<li><a href="https://philipwalton.com/articles/why-im-excited-about-native-css-variables/" target="_blank">Why I'm Excited About Native CSS Variables</a></li>
</ul>
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-55213134146871729112022-07-15T11:21:00.004+08:002023-02-25T18:11:31.288+08:00[轉載] 跨域资源共享 CORS 详解轉載自:<a href="http://www.ruanyifeng.com/blog/2016/04/cors.html" target="_blank">阮一峰 跨域资源共享 CORS 详解</a>
<p>CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。</p>
<p>它允许浏览器向跨源服务器,发出<a href="https://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html" target="_blank"><code>XMLHttpRequest</code></a>请求,从而克服了AJAX只能<a href="https://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html" target="_blank">同源</a>使用的限制。</p>
<p>本文详细介绍CORS的内部机制。</p>
<h3>一、简介</h3>
<p>CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。</p>
<p>整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。</p>
<p>因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。</p>
<h3>二、两种请求</h3>
<p>浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。</p>
<p>只要同时满足以下两大条件,就属于简单请求。</p>
<dl>
<dt>(1) 请求方法是以下三种方法之一:</dt>
<dd>HEAD</dd>
<dd>GET</dd>
<dd>POST</dd>
<dt>(2)HTTP的头信息不超出以下几种字段:</dt>
<dd>Accept</dd>
<dd>Accept-Language</dd>
<dd>Content-Language</dd>
<dd>Last-Event-ID</dd>
<dd>Content-Type:只限于三个值<br/>= <code>application/x-www-form-urlencoded</code><br/>= <code>multipart/form-data</code><br/>= <code>text/plain</code></dd>
</dl>
<p>这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是,只要表单可以发,AJAX 就可以直接发。</p>
<p>凡是不同时满足上面两个条件,就属于非简单请求。</p>
<p>浏览器对这两种请求的处理,是不一样的。</p>
<h3>三、简单请求</h3>
<br/>
<h4>3.1 基本流程</h4>
<p>对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个<code>Origin</code>字段。</p>
<p>下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个<code>Origin</code>字段。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
</pre>
<p>上面的头信息中,<code>Origin</code>字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。</p>
<p>如果<code>Origin</code>指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含<code>Access-Control-Allow-Origin</code>字段(详见下文),就知道出错了,从而抛出一个错误,被<code>XMLHttpRequest</code>的<code>onerror</code>回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。</p>
<p>如果<code>Origin</code>指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
</pre>
<p>上面的头信息之中,有三个与CORS请求相关的字段,都以<code>Access-Control-</code>开头。</p>
<dl>
<dt>(1)Access-Control-Allow-Origin</dt>
<dd>该字段是必须的。它的值要么是请求时<code>Origin</code>字段的值,要么是一个<code>*</code>,表示接受任意域名的请求。</dd>
<dt>(2)Access-Control-Allow-Credentials</dt>
<dd>该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为<code>true</code>,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为<code>true</code>,如果服务器不要浏览器发送Cookie,删除该字段即可。</dd>
<dt>(3)Access-Control-Expose-Headers</dt>
<dd>该字段可选。CORS请求时,<code>XMLHttpRequest</code>对象的<code>getResponseHeader()</code>方法只能拿到6个基本字段:<code>Cache-Control</code>、<code>Content-Language</code>、<code>Content-Type</code>、<code>Expires</code>、<code>Last-Modified</code>、<code>Pragma</code>。如果想拿到其他字段,就必须在<code>Access-Control-Expose-Headers</code>里面指定。上面的例子指定,<code>getResponseHeader('FooBar')</code>可以返回<code>FooBar</code>字段的值。</dd>
</dl>
<h4>3.2 withCredentials 属性</h4>
<p>上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定<code>Access-Control-Allow-Credentials</code>字段。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
Access-Control-Allow-Credentials: true
</pre>
<p>另一方面,开发者必须在AJAX请求中打开<code>withCredentials</code>属性。</p>
<pre class="js:nogutter:nocontrols" name="code">
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
</pre>
<p>否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。</p>
<p>但是,如果省略<code>withCredentials</code>设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭<code>withCredentials</code>。</p>
<pre class="js:nogutter:nocontrols" name="code">
xhr.withCredentials = false;
</pre>
<p>需要注意的是,如果要发送Cookie,<code>Access-Control-Allow-Origin</code>就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的<code>document.cookie</code>也无法读取服务器域名下的Cookie。</p>
<h3>四、非简单请求</h3>
<br/>
<h4>4.1 预检请求</h4>
<p>非简单请求是那种对服务器有特殊要求的请求,比如请求方法是<code>PUT</code>或<code>DELETE</code>,或者<code>Content-Type</code>字段的类型是<code>application/json</code>。</p>
<p>非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。</p>
<p>浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的<code>XMLHttpRequest</code>请求,否则就报错。</p>
<p>下面是一段浏览器的JavaScript脚本。</p>
<pre class="js:nogutter:nocontrols" name="code">
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
</pre>
<p>上面代码中,HTTP请求的方法是<code>PUT</code>,并且发送一个自定义头信息<code>X-Custom-Header</code>。</p>
<p>浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
</pre>
<p>"预检"请求用的请求方法是<code>OPTIONS</code>,表示这个请求是用来询问的。头信息里面,关键字段是<code>Origin</code>,表示请求来自哪个源。</p>
<p>除了<code>Origin</code>字段,"预检"请求的头信息包括两个特殊字段。</p>
<dl>
<dt>(1)Access-Control-Request-Method</dt>
<dd>该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是<code>PUT</code>。</dd>
<dt>(2)Access-Control-Request-Headers</dt>
<dd>该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是<code>X-Custom-Header</code>。</dd>
</dl>
<h4>4.2 预检请求的回应</h4>
<p>服务器收到"预检"请求以后,检查了<code>Origin</code>、<code>Access-Control-Request-Method</code>和<code>Access-Control-Request-Headers</code>字段以后,确认允许跨源请求,就可以做出回应。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
</pre>
<p>上面的HTTP回应中,关键的是<code>Access-Control-Allow-Origin</code>字段,表示<code>http://api.bob.com</code>可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
Access-Control-Allow-Origin: *
</pre>
<p>如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被<code>XMLHttpRequest</code>对象的<code>onerror</code>回调函数捕获。控制台会打印出如下的报错信息。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
</pre>
<p>服务器回应的其他CORS相关字段如下。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
</pre>
<dl>
<dt>(1)Access-Control-Allow-Methods</dt>
<dd>该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。</dd>
<dt>(2)Access-Control-Allow-Headers</dt>
<dd>如果浏览器请求包括<code>Access-Control-Request-Headers</code>字段,则<code>Access-Control-Allow-Headers</code>字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。</dd>
<dt>(3)Access-Control-Allow-Credentials</dt>
<dd>该字段与简单请求时的含义相同。</dd>
<dt>(4)Access-Control-Max-Age</dt>
<dd>该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。</dd>
</dl>
<h4>4.3 浏览器的正常请求和回应</h4>
<p>一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个<code>Origin</code>头信息字段。服务器的回应,也都会有一个<code>Access-Control-Allow-Origin</code>头信息字段。</p>
<p>下面是"预检"请求之后,浏览器的正常CORS请求。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
</pre>
<p>上面头信息的<code>Origin</code>字段是浏览器自动添加的。</p>
<p>下面是服务器正常的回应。</p>
<pre class="cfg:nogutter:nocontrols" name="code">
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
</pre>
<p>上面头信息中,<code>Access-Control-Allow-Origin</code>字段是每次回应都必定包含的。</p>
<h3>五、与JSONP的比较</h3>
<p>CORS与JSONP的使用目的相同,但是比JSONP更强大。</p>
<p>JSONP只支持<code>GET</code>请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。</p>
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-38934231367297216912018-05-12T23:56:00.001+08:002018-05-13T00:37:45.789+08:00JavaScript 正規表示式 跳脫<pre class="js" name="code">RegExp.escape = function(str) {
return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
};
/* use sample */
new RegExp(RegExp.escape("[te()st]"));
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-65166768462478099202017-03-20T10:13:00.000+08:002017-03-20T10:13:35.074+08:00JavaScript 浮點數計算一直一來都很少處理 JS 精準運算的問題,大部分的時候 UI 有點不準也不會造成太大的問題,這次用 JS 做一些加減的數字計算,遇到小數的時候就出現運算誤差,既然是 JS 計算機不精準是不行的,User 不可能接受的,找了一堆文章後沒一個靠譜的,但重點都是轉換到整數進行運算,再做浮點位移,整個計算最好盡量避免小數的處理,另外的問題就是位數不能太多。<br />
<br />
<br />
<pre class="js" name="code">
function transformInt(num1, num2, padZeno, compute) {
num1 = '' + num1;
num2 = '' + num2;
var p1 = 0, p2 = 0;
try { p1 = num1.split(".")[1].length } catch (e) { }
try { p2 = num2.split(".")[1].length } catch (e) { }
if(padZeno){
while (p1 < p2) { p1++; num1 += '0'; }
while (p2 < p1) { p2++; num2 += '0'; }
}
var int1 = parseInt(num1.replace(".", ""), 10);
var int2 = parseInt(num2.replace(".", ""), 10);
return compute(int1, int2, p1, p2);
}
/*浮點數相加*/
function floatAdd(num1, num2) {
return transformInt(num1, num2, true, function(int1, int2, p1, p2){
return (int1 + int2) / Math.pow(10, p1);
});
}
/*浮點數相減*/
function floatSub(num1, num2) {
return transformInt(num1, num2, true, function(int1, int2, p1, p2){
return (int1 - int2) / Math.pow(10, p1);
});
}
/*浮點數相乘*/
function floatMul(num1, num2) {
return transformInt(num1, num2, false, function(int1, int2, p1, p2){
return (int1 * int2) / Math.pow(10, p1 + p2);
});
}
/*浮點數相除*/
function floatDiv(num1, num2) {
return transformInt(num1, num2, false, function(int1, int2, p1, p2){
return (int1 / int2) / Math.pow(10, p1 - p2);
});
}
function unitTest(method, list){
console.log('=[ ' + method.name + ' ]==============================================');
for (var i = 0; i < list.length; i++){
var row = list[i];
var result = method(row.num1, row.num2);
if(result == row.ans){
console.log(row, result);
}else{
console.error(row, result);
}
};
}
unitTest(floatAdd, [
{num1:0.11, num2:0.2, ans: 0.31},
{num1:0.21, num2:0.2, ans: 0.41},
{num1:0.31, num2:0.2, ans: 0.51},
{num1:0.41, num2:0.2, ans: 0.61},
{num1:0.51, num2:0.2, ans: 0.71},
{num1:0.61, num2:0.2, ans: 0.81},
{num1:0.71, num2:0.2, ans: 0.91},
{num1:0.81, num2:0.2, ans: 1.01},
{num1:0.91, num2:0.2, ans: 1.11},
{num1:1.01, num2:0.2, ans: 1.21},
]);
unitTest(floatSub, [
{num1:1.02, num2:0.2, ans: 0.82},
{num1:1.12, num2:0.2, ans: 0.92},
{num1:1.22, num2:0.2, ans: 1.02},
{num1:1.32, num2:0.2, ans: 1.12},
{num1:1.42, num2:0.2, ans: 1.22},
{num1:1.52, num2:0.2, ans: 1.32},
{num1:1.62, num2:0.2, ans: 1.42},
{num1:1.72, num2:0.2, ans: 1.52},
{num1:1.82, num2:0.2, ans: 1.62},
{num1:1.92, num2:0.2, ans: 1.72},
]);
unitTest(floatMul, [
{num1:10, num2:0.14, ans: 1.4},
{num1:10, num2:0.24, ans: 2.4},
{num1:10, num2:0.34, ans: 3.4},
{num1:10, num2:0.44, ans: 4.4},
{num1:10, num2:0.54, ans: 5.4},
{num1:10, num2:0.64, ans: 6.4},
{num1:10, num2:0.74, ans: 7.4},
{num1:10, num2:0.84, ans: 8.4},
{num1:10, num2:0.94, ans: 9.4},
{num1:10, num2:1.04, ans: 10.4},
]);
unitTest(floatDiv, [
{num1:1.1, num2:0.1, ans: 11},
{num1:1.1, num2:0.2, ans: 5.5},
{num1:0.99, num2:0.3, ans: 3.3},
{num1:1.1, num2:0.4, ans: 2.75},
{num1:1.1, num2:0.5, ans: 2.2},
{num1:1.32, num2:0.6, ans: 2.2},
{num1:1.54, num2:0.7, ans: 2.2},
{num1:1.76, num2:0.8, ans: 2.2},
{num1:2.97, num2:0.9, ans: 3.3},
{num1:1.1, num2:0.1, ans: 11},
]);
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-58421091557936592602015-11-26T14:57:00.001+08:002018-03-20T13:48:57.966+08:00天文鐘<embed id="body-layer" src="https://weskerjax.github.io/astronomical/index.svg" type="image/svg+xml" wmode="transparent"></embed>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-8991216962887549852014-11-10T11:45:00.000+08:002014-11-10T11:52:14.724+08:00[轉載] Javascript Char Codes (Key Codes)轉載自:<a href="http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes" target="_blank">Javascript Char Codes (Key Codes)</a><br />
<br />
<table class="table_list" cellspacing="0" cellpadding="4" border="1" style="width:150px; float:left; margin-right:10px;"><tr class="header"><th>Key</th><th>Code</th></tr>
<tr><td>backspace</td><td>8</td></tr>
<tr><td>tab</td><td>9</td></tr>
<tr><td>enter</td><td>13</td></tr>
<tr><td>shift</td><td>16</td></tr>
<tr><td>ctrl</td><td>17</td></tr>
<tr><td>alt</td><td>18</td></tr>
<tr><td>pause/break</td><td>19</td></tr>
<tr><td>caps lock</td><td>20</td></tr>
<tr><td>escape</td><td>27</td></tr>
<tr><td>page up</td><td>33</td></tr>
<tr><td>page down</td><td>34</td></tr>
<tr><td>end</td><td>35</td></tr>
<tr><td>home</td><td>36</td></tr>
<tr><td>left arrow</td><td>37</td></tr>
<tr><td>up arrow</td><td>38</td></tr>
<tr><td>right arrow</td><td>39</td></tr>
<tr><td>down arrow</td><td>40</td></tr>
<tr><td>insert</td><td>45</td></tr>
<tr><td>delete</td><td>46</td></tr>
<tr><td>0</td><td>48</td></tr>
<tr><td>1</td><td>49</td></tr>
<tr><td>2</td><td>50</td></tr>
<tr><td>3</td><td>51</td></tr>
<tr><td>4</td><td>52</td></tr>
<tr><td>5</td><td>53</td></tr>
<tr><td>6</td><td>54</td></tr>
<tr><td>7</td><td>55</td></tr>
<tr><td>8</td><td>56</td></tr>
<tr><td>9</td><td>57</td></tr>
<tr><td>a</td><td>65</td></tr>
<tr><td>b</td><td>66</td></tr>
<tr><td>c</td><td>67</td></tr>
<tr><td>d</td><td>68</td></tr>
</table><table class="table_list" cellspacing="0" cellpadding="4" border="1" style="width:150px; float:left; margin-right:10px;"><tr class="header"><th>Key</th><th>Code</th></tr>
<tr><td>e</td><td>69</td></tr>
<tr><td>f</td><td>70</td></tr>
<tr><td>g</td><td>71</td></tr>
<tr><td>h</td><td>72</td></tr>
<tr><td>i</td><td>73</td></tr>
<tr><td>j</td><td>74</td></tr>
<tr><td>k</td><td>75</td></tr>
<tr><td>l</td><td>76</td></tr>
<tr><td>m</td><td>77</td></tr>
<tr><td>n</td><td>78</td></tr>
<tr><td>o</td><td>79</td></tr>
<tr><td>p</td><td>80</td></tr>
<tr><td>q</td><td>81</td></tr>
<tr><td>r</td><td>82</td></tr>
<tr><td>s</td><td>83</td></tr>
<tr><td>t</td><td>84</td></tr>
<tr><td>u</td><td>85</td></tr>
<tr><td>v</td><td>86</td></tr>
<tr><td>w</td><td>87</td></tr>
<tr><td>x</td><td>88</td></tr>
<tr><td>y</td><td>89</td></tr>
<tr><td>z</td><td>90</td></tr>
<tr><td>left window key</td><td>91</td></tr>
<tr><td>right window key</td><td>92</td></tr>
<tr><td>select key</td><td>93</td></tr>
<tr><td>numpad 0</td><td>96</td></tr>
<tr><td>numpad 1</td><td>97</td></tr>
<tr><td>numpad 2</td><td>98</td></tr>
<tr><td>numpad 3</td><td>99</td></tr>
<tr><td>numpad 4</td><td>100</td></tr>
<tr><td>numpad 5</td><td>101</td></tr>
<tr><td>numpad 6</td><td>102</td></tr>
<tr><td>numpad 7</td><td>103</td></tr>
</table><table class="table_list" cellspacing="0" cellpadding="4" border="1" style="width:150px; float:left; margin-right:10px;"><tr class="header"><th>Key</th><th>Code</th></tr>
<tr><td>numpad 8</td><td>104</td></tr>
<tr><td>numpad 9</td><td>105</td></tr>
<tr><td>multiply</td><td>106</td></tr>
<tr><td>add</td><td>107</td></tr>
<tr><td>subtract</td><td>109</td></tr>
<tr><td>decimal point</td><td>110</td></tr>
<tr><td>divide</td><td>111</td></tr>
<tr><td>f1</td><td>112</td></tr>
<tr><td>f2</td><td>113</td></tr>
<tr><td>f3</td><td>114</td></tr>
<tr><td>f4</td><td>115</td></tr>
<tr><td>f5</td><td>116</td></tr>
<tr><td>f6</td><td>117</td></tr>
<tr><td>f7</td><td>118</td></tr>
<tr><td>f8</td><td>119</td></tr>
<tr><td>f9</td><td>120</td></tr>
<tr><td>f10</td><td>121</td></tr>
<tr><td>f11</td><td>122</td></tr>
<tr><td>f12</td><td>123</td></tr>
<tr><td>num lock</td><td>144</td></tr>
<tr><td>scroll lock</td><td>145</td></tr>
<tr><td>semi-colon</td><td>186</td></tr>
<tr><td>equal sign</td><td>187</td></tr>
<tr><td>comma</td><td>188</td></tr>
<tr><td>dash</td><td>189</td></tr>
<tr><td>period</td><td>190</td></tr>
<tr><td>forward slash</td><td>191</td></tr>
<tr><td>grave accent</td><td>192</td></tr>
<tr><td>open bracket</td><td>219</td></tr>
<tr><td>back slash</td><td>220</td></tr>
<tr><td>close braket</td><td>221</td></tr>
<tr><td>single quote</td><td>222</td></tr>
<tr><td> </td><td> </td></tr>
</table>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-52279852424499773042014-10-02T09:48:00.000+08:002014-10-02T09:48:42.537+08:00[轉載] Superpower your JavaScript with 10 quick tips - For Beginners轉載自:<a target="_blank" href="http://www.htmlxprs.com/post/14/superpower-your-javascript-with-10-quick-tips">Superpower your JavaScript with 10 quick tips - For Beginners</a><br />
<br />
Who doesn't love to write better code? In this article I will list 10 simple and quick JavaScript tips that will help beginners improve their code quality. You might know some of these already. In case you didn't feel free to buy me a beer later. <br />
<br />
<br />
<h3>#1 - Use && and || Operators Like a Boss</h3><br />
Well, JavaScript gives you two awesome logical operators : <code>&&</code> and <code>||</code>. In other languages like Java or C++ they always return a boolean value. But things get a bit interesting when it comes to JavaScript. The <code>&&</code> and <code>||</code> operators return the last evaluated operand. Have a look at the following<br />
<br />
<pre class="js:nogutter:nocontrols" name="code">function getMeBeers(count){
if(count){
return count;
}
else{
return 1;
}
}
</pre><br />
Well, our function is simple and returns the required number of beers. If you specify no argument, it simply gives you a single beer. Here, you can use <code>||</code> to refactor the code as following:<br />
<br />
<pre class="js:nogutter:nocontrols" name="code">function getMeBeers(count){
return count || 1;
}
</pre><br />
In case of <code>||</code> if the first operand is falsy, JavaScript will evaluate the next operand. So, if no count is specified while calling the function, 1 will be returned. On the other hand, if count is specified then it will be returned (as the first operand is truthy in this case).<br />
<br />
<strong>Note:</strong> <code>null</code>,<code> false</code>,<code>0</code> (the number), <code>undefined</code>, <code>NaN</code>, and <code>''</code> (empty string) are falsy values. Any other value is considered truthy.<br />
<br />
Now if you want to allow adults only, you can use <code>&&</code> as following:<br />
<br />
<pre class="js:nogutter:nocontrols" name="code">function getMeBeers(age, count){
return (age>=18) && (count || 1);
}
</pre><br />
Instead of : <br />
<br />
<pre class="js:nogutter:nocontrols" name="code">function getMeBeers(age, count){
if(age>=18){
return count || 1;
}
}</pre><br />
In the above case if age < 18, JavaScript won't even bother evaluating the next operand. So, our function will return. But if the age is actually >= 18, the first operand will evaluate to <code>true</code> and then the next operand will be evaluated.<br />
<br />
<blockquote>By the way you should be a careful here. If you pass count as 0, the function will return 1 (as 0 is falsy). So, in a real world app you should be careful while handling numeric values.</blockquote><br />
<br />
<br />
<h3>#2 - Use === and !== Instead of == and !=</h3><br />
The operators <code>==</code> and <code>!=</code> do automatic type conversion, if needed. But <code>===</code> and <code>!==</code> will consider both value and type while comparing and won't do any automatic type conversion. So, to reliably compare two values for equality/non-equality always use <code>===</code> and <code>!==</code>. <br />
<br />
<pre class="js:nogutter:nocontrols" name="code">10 == '10' //true
10 === '10' //false
10 === 10 //true
</pre><br />
<br />
<br />
<h3>#3 - Use Strict mode while writing JS</h3><br />
Strict mode was introduced in ECMA 5. It allows you to put a function or an entire script into strict operating context. The benefits are:<br />
<br />
<ol><li>It eliminates some of the silent JavaScript errors by throwing the errors explicitly. </li>
<li>It throws exceptions when relatively unsafe actions take place.</li>
<li>Sometimes strict mode code can run faster than non strict mode code.</li>
</ol><br />
<pre class="xml:nogutter:nocontrols" name="code"><script type="text/javascript">
'use strict'
//Strict mode code goes here
</script>
</pre><br />
Or,<br />
<br />
<pre class="js:nogutter:nocontrols" name="code">function strictCode(){
'use strict'
//This is a strict mode function
}
</pre><br />
As currently all the major browsers <a href="http://caniuse.com/#feat=use-strict" target="_blank">support</a> this feature you should start using strict mode.<br />
<br />
<br />
<br />
<h3>#4 - Use splice() to remove an array element</h3><br />
If you want to remove an element from an array use <code>splice()</code> instead of <code>delete</code>. <code>delete</code> sets the particular array item to <code>undefined</code> while <code>splice()</code> actually removes the item. <br />
<br />
<pre class="js:nogutter:nocontrols" name="code">var array=[1,2,3];
delete array[1]; // now array= [1,undefined,3]
array.splice(1,1); //now array=[1,3]
</pre><br />
<br />
<br />
<h3>#5 - Use [] to create new array</h3><br />
Use <code>[]</code> to create a new array. Writing <code>var a=[1,2,3]</code> will create a 3-element array whereas <code>new Array(N)</code> will create a physically empty array of N logical length.<br />
<br />
Similarly, use <code>var a={}</code> to create an object rather than <code>new Object()</code>. The former one is compact, readable and takes less space. You can also instantly populate the object like:<br />
<br />
<pre class="js:nogutter:nocontrols" name="code">var a = {
name: 'John Doe',
email: 'john@doe.com'
}
</pre><br />
<br />
<br />
<h3>#6 - Use String.link() to create Hyperlinks</h3><br />
Many times in JavaScript you will need to generate HTML anchors on the fly. For that you will need some concatenation which may look ugly and messy. <br />
<br />
<pre class="js:nogutter:nocontrols" name="code">var text="Cool JS Tips";
var url="http://www.htmlxprs.com";
var block='<a href="'+url+'">'+ text +'</a>';
</pre><br />
Instead, how about this: <br />
<br />
<pre class="js:nogutter:nocontrols" name="code">var block=text.link(url); // <a href="http://www.htmlxprs.com">Cool JS Tips</a>
</pre><br />
<br />
<br />
<h3>#7 - Cache the length while looping</h3><br />
While looping through a JavaScript array you can cache the length so that the overall performance is better:<br />
<br />
<pre class="js:nogutter:nocontrols" name="code">for (var i=0,length=array.length; i<length; i++){
//awesome code goes here
}
</pre><br />
Be careful while creating an inner loop. You need to name the length variable differently in the inner one. <br />
<br />
<br />
<br />
<h3>#8 - Put 'var' before your variable name</h3><br />
While creating variables don't forget to use the <code>var</code> keyword. If you forget then the variable gets added to the global scope which is definitely a bad thing.<br />
<br />
<pre class="js:nogutter:nocontrols" name="code">var localVar=12; //this is a local variable
globalVar=12; //variable goes to 'window' global
</pre><br />
<br />
<br />
<h3>#9 - Use Closures and Self Executing Functions</h3><br />
Closures, if used carefully and precisely, can take your JS code to a whole new level. There are plenty of tutorials available which can teach you about <a href="http://stackoverflow.com/questions/111102/how-do-javascript-closures-work" target="_blank">closures</a>. I will just give an example of closures and self executing functions for creating modular code:<br />
<br />
<pre class="js:nogutter:nocontrols" name="code">var car=(function(){
var _name='Benz Velo';
return {
getName: function(){
return _name;
}
}
})(); //function created and invoked
console.log(car.getName()); //Logs Benz Velo
</pre><br />
As you see we just created a function and immediately invoked it. These are called self executing functions or Immediately Invoked Function Expression (IIFE). The function returns an object which is stored in variable <code>car</code>. We also used closure because the object being returned can access the variable <code>_name</code> defined by the parent function. This is helpful for creating modules and emulating private variables.<br />
<br />
<br />
<br />
<h3>#10 - There is always room for Optimization</h3><br />
Use jsHint or jsLint to check your code quality and improve further. Also don't forget to minify and concatenate your JS files while deploying your app. Finally, use <a href="http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/" target="_blank">SourceMaps</a> to make debugging a breeze.<br />
<br />
<br />
<br />
<h3>Conclusion</h3><br />
Tips can be unlimited. But I hope these 10 quick tips will help you write better code and superpower your JS-Foo. If you loved this you will love our newsletter as well. Don't forget to subscribe.<br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-57148724996672835022014-05-08T02:58:00.000+08:002014-05-08T02:58:09.841+08:00[AngularJS] 製作 jQuery UI Sortable directiveHtml<br />
<pre class="xml" name="code"><div jq-sortable="selectedList">
<div ng-repeat="item in selectedList">
<img ng-src="{{item.src}}" />
</div>
</div>
</pre><br />
<br />
JavaScript<br />
<pre class="js" name="code">app.directive('jqSortable', ['$parse', function($parse) {
return function(scope, element, attrs) {
/*解析並取得表達式*/
var expr = $parse(attrs['jqSortable']);
var $oldChildren;
element.sortable({
opacity: 0.7,
scroll: false,
tolerance: "pointer",
start: function() {
/*紀錄移動前的 children 順序*/
$oldChildren = element.children('[ng-repeat]');
},
update: function(){
var newList = [];
var oldList = expr(scope);
var $children = element.children('[ng-repeat]');
/*產生新順序的陣列*/
$oldChildren.each(function(i){
var index = $children.index(this);
if(index == -1){ return; }
newList[index] = oldList[i];
});
/*將新順序的陣列寫回 scope 變數*/
expr.assign(scope, newList);
/*通知 scope 有異動發生*/
scope.$digest();
}
});
/*在 destroy 時解除 Sortable*/
scope.$on('$destroy', function(){
element.sortable('destroy');
});
};
}]);
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-82000849991912917322014-05-08T02:56:00.000+08:002014-05-08T02:58:56.043+08:00[AngularJS] 製作 Mouse Drag Event directiveHtml<br />
<pre class="xml" name="code"><div ng-style="{'top': itemTop, 'left': itemLeft}"
my-mousedrag="itemTop = itemTop - $deltaY; itemLeft = itemLeft - $deltaX"
></div>
</pre><br />
JavaScript<br />
<pre class="js" name="code">app.directive('myMousedrag', function() {
return function(scope, element, attrs) {
var prevEvent;
element.mousedown(function(event){
prevEvent = event;
}).mouseup(function(event){
prevEvent = null;
}).mousemove(function(event){
if(!prevEvent){ return; }
/*將 element 拖移事件傳遞到 scope 上*/
scope.$eval(attrs['myMousedrag'], {
$event: event,
$deltaX: event.clientX - prevEvent.clientX,
$deltaY: event.clientY - prevEvent.clientY
});
/*通知 scope 有異動發生*/
scope.$digest();
prevEvent = event;
});
/*在 destroy 時清除事件註冊*/
scope.$on('$destroy', function(){
element.off('mousedown mouseup mousemove');
});
};
});
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-14100384141966647202014-05-08T02:54:00.000+08:002014-05-08T02:54:14.702+08:00[AngularJS] 製作 jQuery MouseWheel directive相依套件:<a href="https://github.com/brandonaaron/jquery-mousewheel" target="_blank">jquery-mousewheel</a><br />
<br />
Html<br />
<pre class="xml" name="code"><div jq-mousewheel="changeSize($event, $delta, $deltaX, $deltaY)"></div>
</pre><br />
JavaScript<br />
<pre class="js" name="code">app.directive('jqMousewheel', function(){
return function(scope, element, attrs) {
/*將 element 滾輪事件傳遞到 scope 上*/
element.on('mousewheel', function (event) {
scope.$eval(attrs['jqMousewheel'], {
$event: event,
$delta: event.delta,
$deltaX: event.deltaX,
$deltaY: event.deltaY
});
/*通知 scope 有異動發生*/
scope.$digest();
});
/*在 destroy 時清除事件註冊*/
scope.$on('$destroy', function(){
element.off('mousewheel');
});
};
});
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-26510350574789047362014-05-08T02:52:00.000+08:002014-05-08T03:00:03.540+08:00[AngularJS] 製作 jQuery scrollTop scrollLeft directiveHtml<br />
<pre class="xml" name="code"><div jq-scroll-top="viewerScrollTop"
jq-scroll-left="viewerScrollLeft"
></div>
</pre><br />
JavaScript<br />
<pre class="js" name="code">app.directive('jqScrollTop', ['$parse', function($parse){
return function(scope, element, attrs) {
/*解析並取得表達式*/
var expr = $parse(attrs['jqScrollTop']);
/*監聽變數異動,並更新到 element 上*/
scope.$watch(attrs['jqScrollTop'], function(value) {
element.scrollTop(value);
});
/*當 element 捲動時更新到變數上*/
element.on('scroll', function() {
expr.assign(scope, element.scrollTop());
});
/*在 destroy 時清除事件註冊*/
scope.$on('$destroy', function(){
element.off('scroll');
});
};
}]);
app.directive('jqScrollLeft', ['$parse', function($parse){
return function(scope, element, attrs) {
/*解析並取得表達式*/
var expr = $parse(attrs['jqScrollLeft']);
/*監聽變數異動,並更新到 element 上*/
scope.$watch(attrs['jqScrollLeft'], function(value) {
element.scrollLeft(value);
});
/*當 element 捲動時更新到變數上*/
element.on('scroll', function() {
expr.assign(scope, element.scrollLeft());
});
/*在 destroy 時清除事件註冊*/
scope.$on('$destroy', function(){
element.off('scroll');
});
};
}]);
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-33163877699696296622014-03-05T23:38:00.001+08:002023-02-25T21:32:20.715+08:00利用 HTTP Status Codes 傳遞 Ajax 成功失敗的狀態一般處理 Ajax 回應時會傳送的資訊種類有:資料、成功訊息、錯誤訊息、失敗訊息以及處理狀態,傳遞的資訊種類並不一致,再加上除了資料之外,通常還希望能傳遞處理狀態,這種情況大部分會選擇是以 JSON 的方式傳遞這兩個訊息,以下是常見的幾種格式:<br />
<br />
<pre class="js" name="code">{ code: 1, msg: "OK" }
{ success: true, result: "data", errorMsg: "" }
{ status: 'success', result: [], errorMsg: "" }
//...
</pre><br />
但以執行狀態跟操作行為作一個歸納,可以區分以下幾種回傳結果:<br />
<table class="table_list" cellspacing="0" cellpadding="4" border="1"> <tr class="header">
<th>資料操作</th>
<th>HTTP Method</th>
<th>成功</th>
<th>錯誤/失敗</th>
</tr>
<tr>
<td>檢視(Read)</td>
<td>GET</td>
<td>資料</td>
<td>錯誤/失敗訊息</td>
</tr>
<tr>
<td>新增(Create)<br />
修改(Update)<br />
刪除(Delete)</td>
<td>POST</td>
<td>成功訊息</td>
<td>錯誤/失敗訊息</td>
</tr>
</table><br />
從上面的歸納可以看出規律性,接著只要有方法可以傳送處理的狀態,以及能夠區分資料的種類,其實就單純很多,而 HTTP Status Codes 就是用來傳遞 HTTP 的處理狀態,如果利用這個方式來傳遞自訂的處理狀態,這樣 HTTP Content 就可以很單純傳遞資料,讓資料格式不受限於 JSON,還可以使用其他格式(text, xml, html),而且 XMLHttpRequest 本身就有處理 HTTP Status Codes 的能力,而 jQuery.ajax 也有提供 error status 的處理,所以可以利用這個來定義狀態的處理,在 HTTP Status Codes 有幾個已經定義狀態,很適合用來回傳處理狀態的資訊:<br />
<br />
<table class="table_list" cellspacing="0" cellpadding="4" border="1"> <tr>
<th>400</th>
<td>Bad Request</td>
<td>錯誤的請求</td>
<td>適用在表單內容的錯誤,如必填欄位未填、Email 格式錯誤</td>
</tr>
<tr>
<th>403</th>
<td>Forbidden</td>
<td>沒有權限,被禁止的</td>
<td>適用在沒有登入或權限不足</td>
</tr>
<tr>
<th>500</th>
<td>Internal Server Error</td>
<td>內部服務器錯誤</td>
<td>適用在程式的錯誤</td>
</tr>
</table><br />
<br />
jQuery 接收資訊的範例<br />
<pre class="js" name="code">$.ajax({
type: "POST",
url: document.location,
success: function (data, textStatus, jqXHR) {
alert(data);
},
error: function (jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseText);
}
});
</pre> <br />
<br />
PHP 傳遞錯誤訊息的範例 <br />
<pre class="php" name="code">if (php_sapi_name() == 'cgi'){
header("Status: 400 Bad Request");
}else{
header("HTTP/1.0 400 Bad Request");
}
exit("儲存失敗!!");
</pre><br />
<br />
C# MVC 傳遞錯誤訊息的範例 <br />
<pre class="cs" name="code">Response.TrySkipIisCustomErrors = true;
Response.StatusCode = 400;
return Content("儲存失敗!!");
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-54119043130692298212014-02-18T22:46:00.000+08:002014-02-19T13:43:23.716+08:00[jQuery] $.log 與 $.fn.dump記錄一下,偷懶的 console.log<br />
<pre class="js" name="code">if ($ && $.fn) {
$.log = (window['console'] && typeof(console.log)=='function') ?
function () { console.log.apply(console, arguments); } :
$.noop;
$.fn.dump = function (tag) {
var args = Array.prototype.slice.call(arguments);
args.push(this);
$.log.apply($, args);
return this;
}
}
// use
$.log('ok');
$('img').dump();
$('div').dump('my tag');
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-36055306698634161272014-02-18T22:26:00.000+08:002014-02-19T01:34:55.349+08:00[jQuery] 製作 $.fn.delayAll<a href="http://jquery.com/" target="_blank">jQuery</a> 有提供 <a href="http://api.jquery.com/delay/" target="_blank">delay</a> 這個方法,可惜只能用在動畫操作上,想要做到下面的事情就只能用 setTimeout 了。<br />
<pre class="js" name="code">// not working
$('div').css('color','red').delay(200).css('color','blue');
// use setTimeout
$('div').css('color','red');
setTimeout(function(){
$('div').css('color','blue');
}, 200);
</pre><br />
<br />
稍微研究了一下,發現 <a href="http://jquery.com/" target="_blank">jQuery</a> 本身有製作 <a href="http://api.jquery.com/queue/" target="_blank">queue</a> 的功能,這個用來存放 delay 後要執行動作的紀錄器,最後只要對 jQuery Object 作一個代理器的包裝,就可以達到想要的目的了。<br />
<pre class="js" name="code">var queueName = 'delayAll';
/*定義 jQuery Delay 代理類別*/
function jQueryDelay(jqObject, duration){
/*將缺少的成員屬性補上*/
for(var member in jqObject){
if(!$.isFunction(jqObject[member]) || !this[member]){
this[member] = jqObject[member];
}
}
/*新增 delay 時間並啟動 queue*/
jqObject
.delay(duration, queueName)
.dequeue(queueName);
/*紀錄 jQuery 物件*/
this._jqObject = jqObject;
};
/*為所有的 jQuery 方法製作 proxy*/
for(var member in $.fn){
if(!$.isFunction($.fn[member])){ continue; }
jQueryDelay.prototype[member] = function(){
var jqObject = this._jqObject;
var args = Array.prototype.slice.call(arguments);
var mothed = arguments.callee.methodName;
/*將需要執行動作加入 queue*/
jqObject.queue(queueName, function(next) {
jqObject[mothed].apply(jqObject, args);
next();
});
return this;
};
/*紀錄方法名稱,在 proxy 時會需要參考到*/
jQueryDelay.prototype[member].methodName = member;
}
/*允許多次串接的可能*/
jQueryDelay.prototype.delayAll = function(duration){
this._jqObject.delay(duration, queueName);
return this;
};
/*用 jQueryDelay 將原本的 jQuery Object 包裝起來*/
$.fn.delayAll = function(duration){
return new jQueryDelay(this ,duration);
};
</pre><br />
<br />
使用範例:<br />
<pre class="js" name="code">$('div').css('color','#f00')
.delayAll(2000).css('color','#0f0')
.delayAll(2000).css('color','#00f');
</pre><br />
<br />
檔案下載:<a href="https://dl.dropboxusercontent.com/u/16784943/code-demo/jquery.delayAll.js" target="_blank">jquery.delayAll.js</a>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-33268882650467317392014-01-21T01:02:00.001+08:002014-01-21T14:13:01.565+08:00[JavaScript] 匿名 function 進階應用前幾天 JS 新手村的同事說看不懂下面的程式,下面用了幾個問句解釋了他的疑惑。<br />
<pre class="js:nogutter:nocontrols" name="code">(function(model){
//...
})(model || model={});
</pre><br />
JS 要如何宣告一個 function 呢?<br />
<pre class="js:nogutter:nocontrols" name="code">function a(){
//...
}
</pre><br />
那改成變數的方式宣告呢?<br />
<pre class="js:nogutter:nocontrols" name="code">var a = function(){
//...
};
</pre><br />
要怎麼呼叫上面宣告的 function?<br />
<pre class="js:nogutter:nocontrols" name="code">a();
</pre><br />
再來把 a 換成等於右邊的 function 宣告。<br />
<pre class="js:nogutter:nocontrols" name="code">function(){
//...
}();
</pre><br />
上面的寫法不符合語法,再加上 () 吧!<br />
<pre class="js:nogutter:nocontrols" name="code">(function(){
//...
})();
</pre><br />
<br />
接著 model || model={} 是透過 or 判斷句的特性,遇到第一個為真就回傳,看看下面的例子:<br />
<pre class="js:nogutter:nocontrols" name="code">var a = 1 || 2 || 3; // 1
var a = 0 || 2 || 3; // 2
var a = 0 || 0 || 3; // 3
</pre><br />
<br />
<br />
為什麼要用這種寫法呢??<br />
<br />
首先 JS 沒有所謂的 block 變數,在 for 內宣告的變數,外面也能直接存取,唯一能侷限變數存取域的只有 function,在 function 內宣告的變數只有內部能存取,外部是看不見的,所以透過這個方法可以避免變數命名衝突的問題。<br />
<br />
再來透過傳入 function 的變數,外部是無法在更動的,變數的參考點在呼叫 function 就固定下來,外部無法在異動內部所存取的參考點,這樣可以避免參考的變數內容被其他套件程式修改,而造成執行錯誤。<br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-70888430190755736732011-12-21T19:03:00.000+08:002013-06-10T22:43:35.566+08:00[轉載] Aptana Scripting 一個背景處理的範例轉載自:<a target="_blank" href="http://blogs.infosupport.com/monkeying-with-eclipse/">Monkeying with Eclipse | Info Support</a><br />
<br />
<pre class="js" name="code">/*
* Menu: Info Support > Find commented code
* Kudos: Peter Hendriks
* License: EPL 1.0
* DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript
*/
function main() {
loadBundle("org.eclipse.core.jobs");
var ProgressMonitorDialog = Packages.org.eclipse.jface.dialogs.ProgressMonitorDialog;
var IRunnableWithProgress = Packages.org.eclipse.jface.operation.IRunnableWithProgress;
var runnableWithProgress = new IRunnableWithProgress({ run: runCommentSearch });
new ProgressMonitorDialog(window.getShell()).run(true, true, runnableWithProgress);
window.getActivePage().showView("org.eclipse.ui.views.TaskList");
}
function runCommentSearch(monitor) {
var files = resources.filesMatching(".*\\.java");
monitor.beginTask("Searching for commented code...", files.length);
try {
var match;
for each( file in files ) {
monitor.subTask(file.getEclipseObject().getName());
file.removeMyTasks();
var previousLineCodeComment = false;
for each( line in file.lines ) {
if (monitor.isCanceled()) {
return;
}
if (match = line.string.match(/^.*\/\/.*[;{}]\s*$/)) {
if (!previousLineCodeComment) {
line.addMyTask("Commented code: " + match[0]);
}
previousLineCodeComment = true;
} else {
previousLineCodeComment = false;
}
}
monitor.worked(1);
}
} finally {
monitor.done();
}
}
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-30922857090704912682011-12-21T18:58:00.002+08:002011-12-21T19:11:53.662+08:00Aptana Scripting - Find TODOs 範例中的參數<pre class="js" name="code">/*
* Menu: Editors > Find TODOs
* Kudos: Ingo Muschenetz
* License: EPL 1.0
* DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript
*/
function main() {
var files = resources.filesMatching(".*\\.js");
var match;
for each( file in files ) {
file.removeMyTasks( );
for each( line in file.lines ) {
if (match = line.string.match(/\/\/TODO: (.*)/)) {
line.addMyTask( match[1] );
}
}
}
window.getActivePage().showView("org.eclipse.ui.views.TaskList");
}
</pre><br />
<strong>只列出當前文件的方法</strong><br />
<pre class="js:nogutter:nocontrols" name="code">var files = resources.filesMatching('.*/'+editors.activeEditor.textEditor.titleToolTip);
</pre><br />
<strong>resources 部分的方法</strong><br />
<pre class="js:nogutter:nocontrols" name="code">filesMatching(".*\\.js")
filesMatchingForProject("Project Name",".*\\.js")
filesMatchingIgnoreCase(".*\\.js")
filesMatchingForProjectIgnoreCase("Project Name",".*\\.js")
</pre><br />
<strong>file 部分的方法</strong><br />
<pre class="js:nogutter:nocontrols" name="code">size
lines
removeMyTasks()
</pre><br />
<strong>line 部分的方法</strong><br />
<pre class="js:nogutter:nocontrols" name="code">lineNumber
string
addMyTask('Task String')
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0tag:blogger.com,1999:blog-5946530704742130970.post-10881327493269337092011-12-21T18:46:00.000+08:002015-03-13T14:40:27.860+08:00Aptana Scripting 學習筆記<ul><li>在任何一個專案的頂層目錄,建立一個名稱為 scripts 或 monkey 的目錄</li>
<li>在此目錄下建立副檔名為 *.js 或 *.em 的 JavaScript 的文件 </li>
</ul><br />
<strong>一個空白文件的內容如下:</strong><br />
<pre class="js" name="code">/*
* Menu: Samples > Execute Snippet
* Key: M1+M2+M3+F
* Kudos: Jax Hu
* License: EPL 1.0
* DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript
*/
function main(){
}
</pre><br />
<br />
<strong>快捷鍵的代號對應:</strong><br />
<table><tr><td>M1</td><td>Control/Command</td></tr>
<tr><td>M2</td><td>Shift</td></tr>
<tr><td>M3</td><td>Alt/Option</td></tr>
</table><br />
<br />
<strong>一個可以用來顯示物件成員的函數:</strong><br />
<pre class="js" name="code">/**顯示物件的成員
* @param {Object} val 物件
*/
function var_dump(val){
var name, value, temp=[];
for (name in val) {
try {
value = (val[name]+'').
replace("function",'<b style="color:#00f;">$&</b>');
}catch (e) {
value = (e+'').fontcolor('red');
}
temp.push('<b style="color:#707;">'+name+'</b> = '+value);
}
webView = views.getView("var_dump");
webView.showView(true);
webView.setTitle('var_dump');
webView.setHTML(temp.join("<hr/>").fixed());
}
</pre><br />
<br />
<strong>常用方法以及數值:</strong><br />
<pre class="js" name="code">
/*當前文件的位置
* => D:/WorkSpace/my_project/test.js */
location
/*當前文件的名稱
* => test.js */
editors.activeEditor.title
/*當前的文件內容*/
editors.activeEditor.source
/*當前文件的 URI 位址*/
editors.activeEditor.uri
/*儲存當前文件*/
editors.activeEditor.save();
/*對當前文件-開啟另存新檔的對話匡*/
editors.activeEditor.textEditor.doSaveAs()
/*對當前文件的目錄路徑
* => D:/WorkSpace/my_project */
editors.activeEditor.textEditor.editorInput.file.parent.location
/*目前開啟的所有文件*/
editors.all[];
/*儲存全部編輯器,傳入 true 會開啟存檔提示*/
window.workbench.saveAllEditors(false);
/*重新開啟 Eclipse*/
window.workbench.restart();
/*關閉 Eclipse*/
window.workbench.close();
/* 開新的編輯器,可以透過這個開啟空白文件,或是已經存在的檔案,
* 之後 editors.activeEditor 會轉到這個文件上 */
fileUtils.open('textile_to_redmine.txt');
/*取得當前的專案名稱*/
editors.activeEditor.textEditor.editorInput.file.project.name;
/*取得所有的專案*/
Packages.org.eclipse.core.resources.ResourcesPlugin.workspace.root.projects;
editors.activeEditor.textEditor.editorInput.file.workspace.root.projects
</pre><br />
<br />
<strong>建立一個新的視圖,或開啟已存在的視圖:</strong><br />
<pre class="js" name="code">webView = views.getView("my_view_name");
/*顯示視圖*/
webView.showView(true);
/*設定標題*/
webView.setTitle("My View Title");
/*設定內容的HTML*/
webView.setHTML('<h1>OK</h1>');
/*或指定內容的網址*/
webView.url = "http://www.google.com";
webView.addEventListener("LocationChanging", function(event){
var location = event.innerEvent.location;
// Print out the location to the Java console
Packages.java.lang.System.out.println("You clicked on: " + location);
});
</pre><br />
<br />
<strong>替換選擇的文字區段:</strong><br />
<pre class="js" name="code">/*選擇的起始位置*/
var starting = editors.activeEditor.selectionRange.startingOffset;
/*選擇的結束位置*/
var ending = editors.activeEditor.selectionRange.endingOffset;
/*選擇的文字內容*/
var text = editors.activeEditor.source.substring(starting, ending);
/*文字跳脫處理,或其他自訂的處理*/
text = escape(text);
/*替換選擇的文字*/
editors.activeEditor.applyEdit(starting, ending-starting, text);
/*重新選擇文字區段*/
editors.activeEditor.selectAndReveal(starting, text.length);
</pre><br />
<br />
<strong>檔案存取:</strong><br />
<pre class="js" name="code">var file = new File("myFile.txt");
file.createNewFile();
file.write("Date: ");
var text = file.readLines();
</pre><br />
<br />
<strong>Web 資料請求的方式:</strong><br />
<pre class="js" name="code">var req = new WebRequest();
req.open("GET", "http://xml.weather.yahoo.com/forecastrss?p=94103");
var text = req.send();
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com2tag:blogger.com,1999:blog-5946530704742130970.post-80939083545241138592011-12-21T15:05:00.006+08:002013-06-10T22:44:32.104+08:00[轉載] Aptana Scripting File.js Class Document轉載自:<a target="_blank" href="http://www.koders.com/javascript/fid71E40E01E381F207FA6EC48D4B0F2343B95ACCEE.aspx">Koders Code Search: File.js - JavaScript</a><br />
<pre class="js" name="code">/**
* File.js
*
* Adding this file to your Active Libraries will give you code assist for the
* Aptana Studio scripting engine.
*
* @author Kevin Lindsey
* @version 1.0
*/
/**
* This object represents a file or a directory in the file system
*
* @constructor
* @param {String} name
* The relative or absolute path to a file or directory
*/
function File(name) {}
/*
* Properties
*
/**
* Get the absolute path for this file or directory
*
* @type {String} Returns the absolute path to this file or directory
*/
File.prototype.absolutePath = "";
/**
* Get the file's base name. This is the filename only without the extension.
* If the file does not have an extension, then this will return the full
* name
*
* @type {String} Returns this file's base name
*/
File.prototype.baseName = "";
/**
* Determine if this file is readable
*
* @type {Boolean} Returns true if this File is readable
*/
File.prototype.canRead = false;
/**
* Determine if ths file is writable
*
* @type {Boolean} Returns true if this File is writable
*/
File.prototype.canWrite = false;
/**
* Determine if this file or directory exists in the file system
*
* @type {Boolean} Returns true if this File exists in the file system
*/
File.prototype.exists = false;
/**
* Returns the file extension of this File
*
* @type {String} Returns the last instance of "." and the text after it.
* An empty string will be returned if no extension if found. The return
* value includes the '.'
*/
File.prototype.extension = "";
/**
* Determines if thie File is a file in the file system
*
* @type {Boolean} Returns true if this File is a file in the file system
*/
File.prototype.isFile = false;
/**
* Determines if this File is a directory in the file system
*
* @type {Boolean} Returns true if this File is a directory in the file
* system
*/
File.prototype.isDirectory = false;
/**
* Returns a list of File objects for all files in the File. This is equivalent
* to listing out all files in a directory
*
* @type {Array} Returns an array of File objects, one for each file and
* directory in this File
*/
File.prototype.list = [];
/**
* Returns the file's name without path information
*
* @type {String} Returns the file's name
*/
File.prototype.name = "";
/**
* Returns a new File object of this object's parent directory
*
* @type {File} Returns this file's parent File
*/
File.prototype.parentFile = {};
/**
* Returns the character used to separate directories on the underlying OS
*
* @type {String} Returns the directory separator
*/
File.prototype.separator = "";
/*
* Methods
*/
/**
* Create a new file in the file system at the location specified by this
* object
*
* @return {Boolean} Returns true if the file was created successfully.
*/
File.prototype.createNewFile = function() {};
/**
* Return all lines from this File's text file
*
* @return {Array} Returns an array of strings, one for each line in the file.
*/
File.prototype.readLines = function() {};
//eof
</pre>Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0