从实际出发,前端如何解决需求问题!

8,890 阅读4分钟

前言

晚上app上线,下午五点多产品临时提出需求,需要把一个H5页面的二维码图片保存到手机相册!这个二维码是公众号二维码,保存到相册的目的是让用户在微信中能够识别二维码从而关注公众号!

拿到此需求,只有不到3个小时的开发时间,没时间去扯皮也没时间想太多,干就完了!

移动端长按功能

H5图片保存,一般是长按保存,所以,首先要实现一个长按功能。

移动端使用touchstarttouchend来做长按功能,prevent阻止浏览器默认行为,浏览器很多事件有默认行为,不阻止默认行为的话,浏览器会执行它的默认行为,我们用 JavaScript 处理一个事件,通常不需要浏览器动作,所以阻止掉!prevent是vue中的修饰符,关于修饰符的更多内容,可以去这里看事件修饰符

html

<img
    class="code"
    src="@/images/cloudStallUp/xyhd_05@3x.png"
    @touchstart.prevent="touchin"
    @touchend.prevent="cleartime"
  />

js

touchin() {
  clearInterval(this.Loop); //再次清空定时器,防止重复注册定时器
  this.Loop = setTimeout(() => {
   // 这里长按后执行的操作
  }, 1000);
},

// 这个方法主要是用来将每次手指移出之后将计时器清零
cleartime() {
  clearInterval(this.Loop);
},

实现一个dialog弹窗

UI框架基本都有自带的dialog弹窗,如果你的项目使用了某个UI框架,直接使用它的就行。如果你的项目没有使用UI框架,那么就很有必要自己实现一个dialog弹窗

tips:不是所有项目都需要使用UI框架,如果项目非常简单,就不需要,原谅我这轮子党,喜欢自己造轮子,哪怕不好用!

使用H5的dialog标签写弹窗。

html

<dialog ref="dialog">
  <div @click="saveImage">保存图片</div>
  <div @click="closeDialog">取消</div>
</dialog>

css

@function vw($px) {
    @return ($px / 750) * 100vw;
}

$c_dd: #dddddd;

dialog {
  border: none;
  padding: 0;

  div {
    width: vw(750);
    font-size: vw(30);
    height: vw(80);
    line-height: vw(80);

    &:last-child {
      border-top: 1px solid $c_dd;
    }
  }
}

/* 设置背景色 */
dialog::backdrop {
  background: rgba(0, 0, 0, 0.6);
}

js

touchin() {
  clearInterval(this.Loop); //再次清空定时器,防止重复注册定时器
  this.Loop = setTimeout(() => {
   // 显示
   this.$refs.dialog.showModal();
  }, 1000);
},
......

效果展示:

dialog标签的兼容性很有些问题

so,我们需要一个polyfill

安装

npm install dialog-polyfill

在页面中引入

import dialogPolyfill from 'dialog-polyfill'

新建一个dialog-polyfill.scss

dialog {
  position: absolute;
  left: 0; right: 0;
  width: -moz-fit-content;
  width: -webkit-fit-content;
  width: fit-content;
  height: -moz-fit-content;
  height: -webkit-fit-content;
  height: fit-content;
  margin: auto;
  border: solid;
  padding: 1em;
  background: white;
  color: black;
  display: block;
}

dialog:not([open]) {
  display: none;
}

dialog + .backdrop {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  background: rgba(0,0,0,0.1);
}

._dialog_overlay {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
}

dialog.fixed {
  position: fixed;
  top: 50%;
  transform: translate(0, -50%);
}

在页面中引入

@import "@/style/dialog-polyfill.scss";

设置了polyfill 后,背景的css和原生的有所区别

/* 支持dialog */
dialog::backdrop { 
  background-color: green;
}
/* polyfill */
dialog + .backdrop { 
  background-color: green;
}

使用polyfill

js

data() {
    return {
        dialog: null
    };
},
mounted() {
    this.dialog =  this.$refs.dialog;
    dialogPolyfill.registerDialog(this.dialog);
},
methods: {
    touchin() {
      clearInterval(this.Loop); //再次清空定时器,防止重复注册定时器
      this.Loop = setTimeout(() => {
       this.dialog.showModal();
      }, 1000);
    },
    ......
}

保存图片到手机相册

使用a标签保存

// 首先考虑使用a标签下载
saveImage(url) {
  if (!url) {
    return new Error("图片地址不正确");
  }

  let a = document.createElement("a");
  a.href = url;
  a.download = "xxxxx";

  if (document.all) {
    a.click();
  } else {
    // 兼容 Firfox
    var evt = document.createEvent("MouseEvents");
    evt.initEvent("click", true, true);
    a.dispatchEvent(evt);
  }
  this.closeDialog();
},

closeDialog() {
  this.dialog.close();
},

自测发现在pc有用,但是在手机端无用,此方案放弃

使用H5+ API

saveImage() {
    if (!window.plus) return;
    /* eslint-disable */
    plus.gallery.save(
      this.codeImg,
      () => {
        plus.nativeUI.alert("保存图片到相册成功");
      },
      () => {
        plus.nativeUI.alert("保存失败");
      }
    );
    /* eslint-enable */
    this.closeDialog();
},
......

写完后悲催的发现,我们最开始架构设计的时候没有使用这个,无奈下也放弃了。关于更多的相关内容可以去这里了解5+ App开发入门指南

调用app方面原生的能力实现保存功能

在mixin.js中封装

// 调用app方法
// androidName->安卓方法名
// iosName->ios方法名
// androidData->传给安卓的参数
// iosData->传给ios的参数
// h5Fuc->不属于安卓和ios时调用的函数
callAPPFunction(androidName, iosName, androidData, iosData, h5Fuc) {
  switch (true) {
    case this.UTILS.isAndroid:
      window.hitumedia_android_js[androidName](androidData);
      break;
    case this.UTILS.isIOS:
      window.webkit.messageHandlers[iosName].postMessage(iosData);
      break;
    default:
      h5Fuc;
  }
},

在main.js中使用

// 导入全局混入
import Mixin from "@/utils/mixin.js";
Vue.mixin(Mixin);

在页面使用

data() {
    return {
      codeImg: require("@/images/cloudStallUp/xyhd_05@3x.png")
    };
},
...
    saveImage() {
        const url = location.href.split("/")[2];
        // 获取到图片地址,架构原因,我们没有图片服务器,所以暂时只能使用使用项目的地址了
        const postUrl = `http://${url}/activity/${this.codeImg}`;
        this.callAPPFunction("onDownImgs", "", postUrl, "");
        this.closeDialog();
    },
...

OK,完美实现需求。

最后

写完此功能,自测通过后,提交产品验收,然后!!!产品说:“貌似app中很少看见保存二维码的吧,这个功能不要了~~~”

此功能不要了~~~~~~~~~~~~~~~

从这个实际的例子,得出总结,前端如何解决需求问题:

  • 1、来一个需求后,一定要先和产品确定这个需求是否必须
  • 2、一定要和项目经理沟通好时间,如果预估某个时间段完不成,一定要提前说明白
  • 3、不要高估自己的能力,不会的一定不能说会,会的东西,得要留出变量的时间
  • 4、临时需求不要接,让产品去和项目经理沟通
  • 5、开发的时候,要镇定,某一个技术在pc有用,在移动没用或者是在某一个手机没用的情况很正常,换一个技术就行了
  • 6、想法要开阔,某些web端实现不了的功能,就让APP端去做,口才一定要练好
  • 7、不要发怒,不要发怒,一个功能开发完,不要了也很正常,千万不要发怒,fuck!