
すべての人に良い!
Electron-これはとても面白いことで、ハブに関する記事はほとんどありません( habrahabr.ru/post/272075とhabrahabr.ru/post/278951がすぐに見つかりました)。 長い間、私は何かを何とか書きたいと思っていたので、私の手がそれに届きました-同時に、世界には自転車が1つあればもっと多くのことがありました。
つまり、要するに、electronはnode.jsとクロムのハイブリッドです。 なんで? 非常に多様なアプリケーション-強力なGUI(html / js / css)、優れた拡張性(C ++やC#などの他の言語を使用する機能を含む)、jQueryなどのあらゆる種類の機能 一般的に、スタンドアロンのクロスプラットフォームアプリケーションの開発と配布には便利です。
次にアプリケーションについて説明します。 サードパーティのサイトの機能を拡張する基本的な例、 Raphael.js (レンダリング/ svgアニメーション用のグラフィックライブラリ)、 Dancer.js (この場合は音声波形を受信するためのサウンドを視覚化するためのライブラリ)の基本原則を実装します。
プロジェクトの構造から始めましょう。

Package.jsonは、エントリポイント(この例ではindex.js)を持つプロジェクトの説明です。 残りのファイルは、通常、viewsフォルダーに表示されます。この方法では、その目的を示すことを望んでいるのではなく、習慣から名前を付けています。
次に、エントリポイントについて検討します。 現時点では、ノードコンテキストとページコンテンツの連携について、プリロードスクリプト+通常のページナビゲーション、iframeまたはwebviewの3つのアプローチに出会いました。
親ウィンドウからコンテンツを直接操作する機能がないため、率直に言ってWebviewが好きではありません( IPCまたはremote.getGlobalを介してグローバルオブジェクトを使用する必要があります(これは、オブジェクトへのリンクではありませんが、率直に言って、くだらないクローンですたとえば、ウィンドウのコンテンツの操作は機能しません。)ただし、リファラー、ユーザーエージェント、プリロードスクリプト、その他のギミックの代替など、あらゆる種類の便利な機能があります。ページのコンテンツを操作する必要がなく、
Iframeはすでに優れています-コンテンツに直接アクセスできます(Webセキュリティを削減した場合はクロスドメインでも可能)が、window.topをチェックするスタイルの設定がある場合もあります。 ここにnode-webkitのnwfaketopのようなものがあると思いますか? ああ、そうだとすれば。 残念ながら、通常の方法は見つかりませんでした。 window.topチェックやその他の人生の楽しみがないほとんどの場合に最適です。 残念ながら、vkにはそれらがあります。
3番目の方法は、一般におそらく最も単純ですが、制限があります-たとえば、モジュールのチェック/定義/エクスポート(jQueryやRaphaelなど)があるページ内のスクリプトは、Nodピースを見ると喜んで死にます。したがって、ノード統合を削減し、プリロードスクリプトでのみノードコンテキストを使用する必要があります。 ただし、セキュリティにもこれが必要なため、動揺しすぎないでください。
Index.jsのほとんどの部分は、いくつかの例外を除いて、ドックから入手しています。
"use strict";
(function () {
var app = require('app');
var BrowserWindow = require('browser-window');
app.commandLine.appendSwitch('disable-web-security');
app.commandLine.appendSwitch('web-security');
app.commandLine.appendSwitch('allow-displaying-insecure-content');
app.commandLine.appendSwitch('ignore-certificate-errors');
var mainWindow = null;
app.on('window-all-closed', function() {
if (process.platform != 'darwin')
app.quit();
});
app.on('ready', function() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
'web-preferences': {
'web-security': false
},
'node-integration': false,
preload: __dirname + '/views/index.js'
});
mainWindow.webContents.session.webRequest.onBeforeRequest({}, (d,c)=>{
if(d.url.indexOf('http://m.vk.com/js/s_c.js')==0){
var localFile='file://'+__dirname+"/views/jsadditive/s_c.js";
console.log(localFile);
c({redirectURL: localFile});
}
else
c({cancel: false})
});
mainWindow.loadUrl('http://m.vk.com/audios1?performer=1&q=Tonight%20alive');
mainWindow.toggleDevTools();
mainWindow.on('closed', function() {
mainWindow.removeAllListeners();
mainWindow = null;
});
});
}) ();
— appendSwitch. chromium'. 2 , , web-security( , - ). 3 http https , 4 — ( , , , , , ). , 2 4 .
— node-integration:false preload-. , preload . web-preferences, web-security . — .
— onBeforeRequest. / , 1 — dancer.js audio- , , .
loadUrl vk , , … .
views/index.js. .
var fs=require('fs');
var include=(path)=>{
var exports = undefined;
(1,eval)(fs.readFileSync(__dirname+path, 'UTF-8'));
};
var link=(path)=>{
var elem=document.createElement('link');
elem.setAttribute('rel', 'stylesheet');
elem.setAttribute('href', 'file://'+__dirname+path);
document.head.appendChild(elem);
};
window.addEventListener('load', ()=>{
link('/css/index.css');
setTimeout(()=>{
include('/js/jquery-2.2.3.min.js');
include('/js/raphael.js');
include('/js/dancer.min.js');
include('/js/main.js');
}, 100);
});
js'. include , window, , eval , . (1,eval) , — - . , jQuery exports.
link css'.
, / , .
, , . — , .
jQuery('<div>').attr('id', 'output').insertBefore('#au_search_items');
var p=document.querySelector('#output');
var {offsetWidth: w, offsetHeight: h}=p;
var prev;
var time=10;
var mid=h/2;
var step=10;
var chunks=w/step;
var baseArr=[];
var median=0;
var prevtime=0;
var r = Raphael("output", w, h);
var genPath = (x,isClosing=true)=>{
var t=[];
x.forEach((y)=>t.push(...[...y, ' ']));
return t.join(' ')+(isClosing?"z":"");
};
var anim, pathq;
var genRand=()=>{
var arr=[
['M', 0, mid]
];
var baseW = 0;
var i=0;
while(baseW<w)
arr.push(['L', baseW+=step, baseArr[i++]]);
arr.push(['L', w, mid]);
arr.push(['L', w, h]);
arr.push(['L', 0, h]);
pathq=genPath(arr, false);
delete arr;
if(!prev){
prev=r.path(pathq).attr({
//stroke: 'grey',
fill: 'grey'
});
}
/*if(!anim)
anim = Raphael.animation({path: path}, time, "<>");
anim.anim[Object.keys(anim.anim)[0]].path=path;*/
//prev.animate(anim);
prev.attr('path', pathq);
};
var dancer = new Dancer();
window.dancer=dancer;
dancer.bind('update', function(){
var d=Date.now();
if(d-prevtime>time)
prevtime=d;
else
return;
baseArr=[];
var waveForm=Array.from(this.getWaveform());
var chunkLength=waveForm.length/chunks;
while(waveForm.length>0)
baseArr.push((waveForm.splice(0, chunkLength).reduce((a,b)=>a+b)/chunkLength)/((dancer.audio&&(dancer.audio.volume>0))?dancer.audio.volume:1)*h/2+h/2);
requestAnimationFrame(genRand);
});
.
1 , ' . , .
var {offsetWidth: w, offsetHeight: h}=p; — ES6. « . - . - var w=p.offsetWidth. ES6 — , - . — ( , ), .
- genPath. ( )( , this , — arguments), (isClosing=true) spread operator( . , [1,2,3].push(...[4,5]) ->[1,2,3,4,5]. , , . ).
, svg- . .
genRand genRand, genSvgFromThoseAudioWaveformWhateverIsIt. , nobody cares.
, , svg path audio waveforms, baseArr. , stroke (, fill , - ). , cpu( ~20% ~10%).
, Raphael.js(. ).
, , dancer.
, Web audio API. , ( , ).
Callback update , this.getWaveform() 0 1, .
. — requestAnimationFrame. . — .
— baseArr.push. . reduce, , , , .
-, .
— github.com/demogoran/vkvisual.
electron — github.com/electron/electron?utm_content=buffer703cb&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer.
— github.com/electron-userland/electron-prebuilt.
— npm install -g electron-prebuilt + electron. .
, !