1. 程式人生 > >用JavaScript完成頁面自動操作

用JavaScript完成頁面自動操作

  在之前的一篇《JavaScript實現按鍵精靈》中曾記錄了幾個事件物件,本文將會對它們進行一次實戰,要完成的動作包括滾動、點選和翻頁。

一、滾動

  滾動是通過修改容器元素的scrollTop屬性實現的,期間會進行一系列的計算,而每次滾動都會包含一個個小的偏移動作,為了讓這些動作能有序進行,自定義了一個Promise,如下所示。

/**
 * 簡易Promise
 */
var Promise = {
  fns: [],
  then: function(fn) {
    this.fns.push(fn);
    return this;
  },
  resolve: function() {
    if (this.fns.length == 0) return;
    var fn = this.fns.splice(0, 1);
    fn[0] && fn[0].call(this);
  }
};

  為了讓滾動表現的更加順滑,採用了requestAnimationFrame()方法,滾動的方向分為三種,分別是向上、向下或待機,如下所示。

/**
 * 隨機整數
 */
var Util = {
  random: function(max) {
    return Math.floor(Math.random() * max);
  }
};
/**
 * 隨機滾動
 * container 容器元素
 */
function scrollTopBottom(container) {
  var num = Util.random(10);
  for (var i = 0; i < num; i++) {
    (function(count) {
      Promise.then(function() {
        var direction,      //滾動方向
          destination,      //滾動的目標位置
          current,          //當前滾動距離
          slide = 0;        //滾動距離
        destination = Util.random(2000);    
        current = container.scrollTop;        
        direction = Util.random(3);
        (function moveInner() {
          switch (direction) {
            case 0:             //向上滾動
              current += 10;
              break;
            case 1:             //向下滾動
              current -= 10;
              if (current < 0) current = 0;
              break;
            default:            //保持原地
              break;
          }
          slide += 10;          //執行滾動
          console.log(count, slide, current, destination);
          container.scrollTop = current;
          if (slide <= destination && current > 0) {
            window.requestAnimationFrame(moveInner);    //順滑的滾動
          } else {
            Promise.resolve();                          //執行下一個動作
          }
        })();
      });
    })(i);
  }
  Promise.resolve();        //開始滾動
}

  滾動的容器元素多變,可能是body,也可能是根元素或者是其它元素,具體得視頁面而定,示例頁面採用的是根元素,如下所示。

/**
 * document.documentElement
 * document.body
 */
scrollTopBottom(document.documentElement);

  等到的效果如下圖所示。

  雖然完成了自動滾動,但當前的程式碼無法精度控制,例如難以配置成第幾秒向上或向下滾動,或者指定滾動到真實使用者會停留的位置的時間。

二、點選

  在觸發點選事件時,需要指定一些元素。目前的座標是隨機生成的,每次會遍歷元素,當座標在元素範圍內時,才派發事件,完成點選,如下所示。MouseEvent中的clientX、pageX等屬性可參考《觸屏touch事件記錄》中的記錄。

/**
 * 點選
 */
function click() {
  var links = document.querySelectorAll("img"),     //指定要觸發的元素
    x = Util.random(window.outerWidth),             //隨機X座標
    y = Util.random(document.body.scrollHeight),    //隨機Y座標
    clientY = y > window.outerHeight ? (y - window.outerHeight) : y;
  var event = new MouseEvent("click", {
    bubbles: true,       //能夠冒泡
    cancelable: true,    //可以取消事件
    view: window,        //視窗
    clientX: x,
    clientY: clientY,    //相對於視口的垂直偏移
    pageX: x,
    pageY: y             //包含垂直滾動的偏移
  });
  [].forEach.call(links, function(value, key) {
    var rect = value.getBoundingClientRect();
    //判斷當前座標是否在元素範圍內
    if(x >= rect.left && x<=rect.right && y>=rect.top && y<=rect.bottom) {
      console.log(x, y);
      value.dispatchEvent(event);        //派發事件
    }
  });
}
function runClick() {
  for (var j = 0; j < 50; j++) {
    (function(j) {
      setTimeout(function() {
        click();         //隨意點選頁面
      }, 2000 * j);      //不集中在一個時間執行
    })(j);
  }
}

  雖然完成了自動點選,但還不夠靈活。當要觸發的動作不是由指定的元素觸發的時,這段指令碼就起不了作用,並且手機螢幕尺寸眾多,難以精確的在某一指定區域內點選。

  由於很依賴事件型別,因此當繫結的動作不在該事件中時,程式碼也會失效。如果要觸發頁面監測的請求,那麼不得不先去翻原始碼,搜尋觸發事件。

三、翻頁

  現在很多活動頁面都是以全屏翻頁的形式出現,通過touchstart、touchmove和touchend三個事件,就能模擬出手指滑動的效果,方向既可以是從下到上,也可以是從右往左,下面的程式碼採用了前者。使用柯里化的方式減少了touch()函式的引數,TouchEvent中的touches和targetTouches引數,也可以參考《觸屏touch事件記錄》中的記錄。

/**
 * 豎屏翻頁
 */
var identifier = 0,
  eventType = ["touchstart", "touchmove", "touchend"];
function slide(container) {
  var x = Util.random(window.outerWidth),
    y = 300,
    currying = touch(container, x, y);
  currying(eventType[0]);
  currying(eventType[1]);
  currying(eventType[2]);
}
function touch(container, x, y) {
  var interval = 100;                 //滑動距離
  return function(type) {
    identifier++;
    if (type == eventType[1]) {       //touchmove事件更改Y座標
      y -= interval;
    }
    var t = new Touch({
      identifier: identifier,
      target: container,
      clientX: x,
      clientY: y,
      pageX: x,
      pageY: y
    });
    console.log(`${identifier}, ${type} x: ${x}, y: ${y}`);
    var event = new TouchEvent(type, {
      touches: [t],
      targetTouches: [t]
    });
    container.dispatchEvent(event);
  };
}
/**
 * 假設滑動外掛是swiper.js
 * 那麼取其容器作為slide()的引數傳入
 */
setInterval(function() {
  slide(document.querySelector(".swiper-container"));
}, 2000);

  得到的效果如下圖所示。

  

  示例中使用的是swiper觸屏滑動外掛,當換成其他外掛時,容器就需要跟著改變。

  上述所有自動化操作都是基於DOM結構完成的,水能載舟亦能覆舟,無法跳出DOM的限制,就會導致一系列的問題,例如針對不同頁面的結構要做單獨的分析、動作精度難以控制、真實的使用者軌跡難以模擬、程式碼不夠靈活難以複用。

  在實際情況中,還有很多複雜的動作(例如答題、填表單等),光靠上述這點程式碼是遠遠不夠的,目前還做不到像按鍵精靈那樣在螢幕上錄製行為,這麼簡潔。

 

demo程式碼已上傳至GitHub:

https://github.com/pwstrick/auto

&n