版游戏一直占据着游戏界的半壁江山,但是对于H5游戏来说,它本身没有权利要求浏览器横屏,这给横版游戏很大的限制。本文探讨在Phaser中,如何完美地解决这个问题。
Phaser的屏幕适配问题是游戏开发中讨论最多的问题,也是很关键的一个问题。稍微用过Phaser的同学都应该知道了,Phaser中的ScaleManager是专门用来解决Phaser屏幕适配问题的。如果不知道ScaleManager是什么的同学,可以查看官方文档。
在了解了ScaleManager之后,我们还会有两个问题。第一、ScaleManager有没有办法做到不变形,有能充满整个屏幕?第二、怎么让浏览器横屏?
首先,我们来说第一个问题,这其实是一个数学问题。这个问题的本质是,两个宽高比不想等的矩形,有没有办法通过等比缩放让它们完全重叠?相信任何一个小学数学学得还不错的人都能给出答案,不可能。
但是有没有办法解决这个问题呢?当然有,那就是动态设置游戏区域,拿到屏幕大小之后再等比缩放成一个游戏区域大小不就行了。但是这会带来一个问题,那就是你的元素无法按照坐标来定位了,因为你不知道游戏的宽高是多少,它和屏幕尺寸有关。这在大多数情况下会让你很难受,但是在一种情况下不会,那就是你的世界大小比你的游戏区域大,这样,你可以固定世界大小,然后元素按照世界大小来定位。
当然,我们还有别的方法,那就是在html中做一些背景,可以让游戏融入到背景中,而不是出现两边的黑边或者白边。具体使用什么方法,就看大家自己的实际情况了。
接下来说说本文重头戏:横屏适配。
html能不能让浏览器强制横屏,phaser中设置了横屏为什么没有用?答案肯定是不能,也没有用。其实道理很简单,因为浏览器是html的环境,html是没有权利去改变它外在的环境的,至少现在不能。
所以我们会看到很多游戏,打开之后,它建议你横屏玩耍。但是这样其实代价很大,因为很多人并不知道怎么横屏。其实我想要的是这样的效果:
也就是虽然是竖屏,但是游戏以横屏的样子展示,这样,再傻的用户都知道要把手机横过来玩吧?那么当用户横过手机的时候,有两种情况,第一,用户的手机设置了竖屏锁定,即使横过来,其实还是竖屏,这种情况没问题。第二,用户没有设置竖屏锁定,横过来之后,系统切换成横屏,那么我们这个旋转90度的屏幕正好又错位了?答案当然不是。
当屏幕横过来的时候,我们就不旋转90度,游戏依然完美呈现。下面来说说怎么实现的。
这个实现涉及到Phaser底层源码,还有PIXI的一些机制,如果大家看不懂,可以跳过,直接看最后怎么用就行了。
首先,我们要考虑,旋转90度应该怎么做,其实只要把世界旋转90度,就可以了,但是世界旋转了之后,它是按照那个点进行旋转的,旋转之后是否要进行坐标调整?看代码。
Phaser.World.prototype.displayObjectUpdateTransform=function() { if(!game.scale.correct) { this.x=game.camera.y + game.width; this.y=-game.camera.x; this.rotation=Phaser.Math.degToRad(Phaser.Math.wrapAngle(90)); } else { this.x=-game.camera.x; this.y=-game.camera.y; this.rotation=0; } PIXI.DisplayObject.prototype.updateTransform.call(this); }
注意,这里调整坐标的时候,加入了camera的修正,这是因为在世界大小比游戏区域大小大的时候,camera就不一定在(0,0)点了,所以要考虑进去。
加了这个当然不够,还需要在游戏中加入:
game.scale.onOrientationChange.add(function() { if(game.scale.isLandscape) { game.scale.correct=true; game.scale.setGameSize(WIDTH, HEIGHT); } else { game.scale.correct=false; game.scale.setGameSize(HEIGHT, WIDTH); } }, this)
每一次横竖屏变化的时候,我们需要实时切换游戏的宽高,道理也是显而易见的。横屏的时候,你的游戏是1920x1080,竖屏的时候,你的游戏就应该是1080x1920,这样才能正好匹配屏幕。
怎么使用也很简单,在游戏的BootState里面加入这些代码就行,其余的事情和之前的做法一样,只是有一点提醒大家,你的game.width和game.height是多少呢?已经不一定了,所以在元素定位的时候不要用它们去定位。我在tacit中的做法是,全局定义好游戏宽高,后面所有定位按照它来做,就不会有问题了。
好了,横屏适配就说到这里。
前言
文章涉及的内容可能不全面,但量很多,需要慢慢看。来源于各个地方,我花了很长的时间整理,希望对大家有帮助。但是难免会有打字的错误或理解的错误,我会及时的进行修改,旨在能帮到大家,谢谢。
Html相关
1 html语义化
意义:根据内容的结构化(内容语义化),选择合适的标签(代码语义化)便于开发者阅读和写出更优雅的代码的同时让浏览器的爬虫和机器很好地解析。 注意:
1.尽可能少的使用无语义的标签div和span;
2.在语义不明显时,既可以使用div或者p时,尽量用p, 因为p在默认情况下有上下间距,对兼容特殊终端有利;
3.不要使用纯样式标签,如:b、font、u等,改用css设置。
4.需要强调的文本,可以包含在strong或者em标签中(浏览器预设样式,能用CSS指定就不用他们),strong默认样式是加粗(不要用b),em是斜体(不用i);
5.使用表格时,标题要用caption,表头用thead,主体部分用tbody包围,尾部用tfoot包围。表头和一般单元格要区分开,表头用th,单元格用td;
6.表单域要用fieldset标签包起来,并用legend标签说明表单的用途;
7.每个input标签对应的说明文本都需要使用label标签,并且通过为input设置id属性,在lable标签中设置for=someld来让说明文本和相对应的input关联起来。
新标签:
2 meta viewport相关
<!DOCTYPE html> H5标准声明,使用 HTML5 doctype,不区分大小写 <head lang=”en”> 标准的 lang 属性写法 <meta charset=’utf-8′> 声明文档使用的字符编码 <meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1″/> 优先使用 IE 最新版本和 Chrome <meta name=”description” content=”不超过150个字符”/> 页面描述 <meta name=”keywords” content=””/> 页面关键词 <meta name=”author” content=”name, email@gmail.com”/> 网页作者 <meta name=”robots” content=”index,follow”/> 搜索引擎抓取 <meta name=”viewport” content=”initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no”> 为移动设备添加 viewport <meta name=”apple-mobile-web-app-title” content=”标题”> iOS 设备 begin <meta name=”apple-mobile-web-app-capable” content=”yes”/> 添加到主屏后的标题(iOS 6 新增) 是否启用 WebApp 全屏模式,删除苹果默认的工具栏和菜单栏 <meta name=”apple-itunes-app” content=”app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL”> 添加智能 App 广告条 Smart App Banner(iOS 6+ Safari) <meta name=”apple-mobile-web-app-status-bar-style” content=”black”/> <meta name=”format-detection” content=”telphone=no, email=no”/> 设置苹果工具栏颜色 <meta name=”renderer” content=”webkit”> 启用360浏览器的极速模式(webkit) <meta http-equiv=”X-UA-Compatible” content=”IE=edge”> 避免IE使用兼容模式 <meta http-equiv=”Cache-Control” content=”no-siteapp” /> 不让百度转码 <meta name=”HandheldFriendly” content=”true”> 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 <meta name=”MobileOptimized” content=”320″> 微软的老式浏览器 <meta name=”screen-orientation” content=”portrait”> uc强制竖屏 <meta name=”x5-orientation” content=”portrait”> QQ强制竖屏 <meta name=”full-screen” content=”yes”> UC强制全屏 <meta name=”x5-fullscreen” content=”true”> QQ强制全屏 <meta name=”browsermode” content=”application”> UC应用模式 <meta name=”x5-page-mode” content=”app”> QQ应用模式 <meta name=”msapplication-tap-highlight” content=”no”> windows phone 点击无高光 设置页面不缓存 <meta http-equiv=”pragma” content=”no-cache”> <meta http-equiv=”cache-control” content=”no-cache”> <meta http-equiv=”expires” content=”0″>
3 canvas 相关
使用前需要获得上下文环境,暂不支持3d 常用api: 1.fillRect(x,y,width,height)实心矩形 2.strokeRect(x,y,width,height)空心矩形 3.fillText("Hello world",200,200);实心文字 4.strokeText("Hello world",200,300)空心文字 各种东西!!!
新标签兼容低版本
CSS相关
1.盒模型
1.ie盒模型算上border、padding及自身(不算margin),标准的只算上自身窗体的大小 css设置方法如下
/* 标准模型 */ box-sizing:content-box; /*IE模型*/ box-sizing:border-box; 复制代码
2.margin、border、padding、content由外到里 3.几种获得宽高的方式
4.拓展 各种获得宽高的方式
5.边距重叠解决方案(BFC) BFC原理
<section class="top"> <h1>上</h1> 这块margin-bottom:30px; </section> <!-- 给下面这个块添加一个父元素,在父元素上创建bfc --> <div style="overflow:hidden"> <section class="bottom"> <h1>下</h1> 这块margin-top:50px; </section> </div>
css reset 和 normalize.css 有什么区别
居中方法
水平方向上
针对inline, 内联块inline-block, 内联表inline-table, inline-flex元素及img,span,button等元素 .text_div{ text-align:center; } 复制代码 不定宽块状元素居中 .text_div{ margin:0 auto;//且需要设置父级宽度 } 复制代码 通过给父元素设置 float,然后给父元素设置 position:relative 和 left:50%,子元素设置 position:relative 和 left: -50% 来实现水平居中。 .wrap{ float:left; position:relative; left:50%; clear:both; } .wrap-center{ left:-50%; }
垂直居中
单行内联(inline-)元素垂直居中 通过设置内联元素的高度(height)和行高(line-height)相等,从而使元素垂直居中。 .text_div{ height: 120px; line-height: 120px; } 利用表布局 .father { display: table; } .children { display: table-cell; vertical-align: middle; text-align: center; } flex布局 .center-flex { display: flex; flex-direction: column;//上下排列 justify-content: center; } 绝对布局方式 已知高度 .parent { position: relative; } .child { position: absolute; top: 50%; height: 100px; margin-top: -50px; } 未知高度 .parent { position: relative; } .child { position: absolute; top: 50%; transform: translateY(-50%); }
垂直水平居中根据上方结合
flex方式 .parent { display: flex; justify-content: center; align-items: center; } grid方式 .parent { height: 140px; display: grid; } .child { margin: auto; }
css优先级确定
bfc内容见盒模型
如何清除浮动
不清楚浮动会发生高度塌陷:浮动元素父元素高度自适应(父元素不写高度时,子元素写了浮动后,父元素会发生高度塌陷)
.float_div:after{ content:"."; clear:both; display:block; height:0; overflow:hidden; visibility:hidden; } .float_div{ zoom:1 }
自适应布局
思路:
画三角形
#item { width: 0; height: 0; border-left: 50px solid transparent; border-right: 50px solid transparent; border-top: 50px solid transparent; border-bottom: 50px solid blue; background: white; }
link @import导入css
animation
长宽比方案
display相关
JavaScript相关
1 ["1", "2", "3"].map(parseInt)
首先, map接受两个参数, 一个回调函数 callback, 一个回调函数的this值 其中回调函数接受三个参数 currentValue, index, arrary; 而题目中, map只传入了回调函数--parseInt. 其次, parseInt 只接受两个两个参数 string, radix(基数). 本题理解来说也就是key与 index 所以本题即问 parseInt('1', 0); parseInt('2', 1); parseInt('3', 2); parseInt(string, radix) string 必需。要被解析的字符串。 radix 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。 如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。
2 [[3,2,1].reduce(Math.pow), [].reduce(Math.pow)]
arr.reduce(callback[, initialValue]) reduce接受两个参数, 一个回调, 一个初始值. 回调函数接受四个参数 previousValue, currentValue, currentIndex, array 需要注意的是 If the array is empty and no initialValue was provided, TypeError would be thrown. 所以第二个表达式会报异常. 第一个表达式等价于 Math.pow(3, 2)=> 9; Math.pow(9, 1)=>9
3
var ary=[0,1,2]; ary[10]=10; ary.filter(function(x) { return x===undefined;}); 我们看到在迭代这个数组的时候, 首先检查了这个索引值是不是数组的一个属性, 那么我们测试一下. 0 in ary;=> true 3 in ary;=> false 10 in ary;=> true 也就是说 从 3 - 9 都是没有初始化的bug !, 这些索引并不存在与数组中. 在 array 的函数调用的时候是会跳过这些坑的.
4 [typeof null, null instanceof Object]
typeof 返回一个表示类型的字符串. instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上. type result Undefined "undefined" Null "object" Boolean "boolean" Number "number" String "string" Symbol "symbol" Host object Implementation-dependent Function "function" Object "object"
5 js数据类型
1.number;
2.string;
3.boolean;
4.undefined;
5.null;
6.symbol(ES6新增,文章后面有对着新类型的解释)Symbol 生成一个全局唯一的值。
7.Object.(包括Object,Array,Function)
8.BigInt:JavaScript新增 任意精度整数
6 promise 用法
定义 var promise=new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } }); 使用 promise.then(function(value) { // success }, function(error) { // failure }); //等价于: promise.then(function(){ //success }).catch(function(){ //failure }) 复制代码
7 es6 promise ajax
定义 const myHttpClient=url=> { return new Promise((resolve, reject)=> { let client=new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange=handler; client.responseType="json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !==4) { return; } if (this.status===200) { resolve(this.response); } else { reject(new Error(this.statusText)); } } }); }; 使用 myHttpClient('https://www.baidu.com').then(res=> { console.log(res); }).catch(error=> { console.log(error); }); 复制代码
8闭包
function foo(x) { var tmp=3; return function (y) { alert(x + y + (++tmp)); } } var bar=foo(2); // bar 现在是一个闭包 bar(10); 结果是16 es6通常用let const块级作用域代替, 闭包缺点,ie中会引起内存泄漏,严格来说是ie的缺点不是闭包的问题 复制代码
9 什么是立即执行函数?使用立即执行函数的目的是什么?
常见两种方式 1.(function(){...})() (function(x){ console.log(x); })(12345) 2.(function(){...}()) (function(x){ console.log(x); }(12345)) 作用 不破坏污染全局的命名空间,若需要使用,将其用变量传入如 (function(window){...}(window)) 复制代码
10 async/await 语法
作用:异步代码的新方式 promise示例 const makeRequest=()=> { return getJSON() .then(data=> { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData=> { console.log(moreData) return moreData }) } else { console.log(data) return data } }) } async/await示例 const makeRequest=async ()=> { const data=await getJSON() if (data.needsAnotherRequest) { const moreData=await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } } 函数前面多了一个aync关键字。await关键字只能用在aync定义的函数内。async函数会隐式地返回一个promise,该promise的reosolve值就是函数return的值。(示例中reosolve值就是字符串"done")
11 深浅拷贝
let a={ aa: 1, bb: 2, cc: 3, dd: { ee: 5, }, ff: { gg: 6, } }; let d=JSON.parse(JSON.stringify(a));//深复制包含子对象 let c={...a};//拷贝一层但不包含子对象 let b=a;//浅拷贝 b.bb=22; c.cc=33; c.dd.ee=55; d.ff.gg=66; console.log(a); console.log(b); console.log(c); console.log(d);
12数组去重
思路1:定义一个新数组,并存放原数组的第一个元素,然后将元素组一一和新数组的元素对比,若不同则存放在新数组中 思路2:先将原数组排序,在与相邻的进行比较,如果不同则存入新数组。 思路3:利用对象属性存在的特性,如果没有该属性则存入新数组。 思路4(最常用):使用es6 set let arr=[1, 2, 3, 3, 5, 7, 2, 6, 8]; console.log([...new Set(arr)]);
13正则实现trim()功能
function myTrim(str) { let reg=/^\s+|\s+$/g; return str.replace(reg, ""); } console.log(myTrim(' asdf '));
14 JS原型
1.每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性 2.个人粗略理解与python的类方法静态方法实例方法差不多
15 es6 class
面向对象,java中类
16 JS 如何实现继承
1.使用原型继承(既继承了父类的模板,又继承了父类的原型对象。优点是继承了父类的模板,又继承了父类的原型对象,缺点就是父类实例传参,不是子类实例化传参,不符合常规语言的写法) 2.使用call的方式(继承了父类的模板,不继承了父类的原型对象。优点是方便了子类实例传参,缺点就是不继承了父类的原型对象)
17 手写jquery插件
(function ($) { $.fn.myPlugins=function (options) { //参数赋值 options=$.extend(defaults, options);//对象合并 this.each(function () { //执行代码逻辑 }); }; })(jQuery); $(selector).myPlugins({参数});
18 数组合并去重排序
let arr1=[1, 25, 2, 26, 1234, 6, 213]; let arr2=[2, 6, 2134, 6, 31, 623]; let c=[...new Set([...arr1, ...arr2])].sort((a, b)=> { return a - b; });
19 call apply
作用:在函数调用时改变函数的执行上下文也就是this的值 区别:call采用不定长的参数列表,而apply使用一个参数数组。 性能优化图
20 for 中setTimeOut
要为循环题创建不同的循环副本
21 sort函数
V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,数量小于10的数组使用 插入,比10大的数组则使用 快排。
22 navigator
23 jquery绑定方式
24 事件流向
25原生操作class
//判断有无 function hasClass(ele, cls) { return ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)")); } //添加 function addClass(ele, cls) { if (!this.hasClass(ele, cls)) ele.className +=" " + cls; } //删除 function removeClass(ele, cls) { if (hasClass(ele, cls)) { let reg=new RegExp("(\\s|^)" + cls + "(\\s|$)"); ele.className=ele.className.replace(reg, " "); } } html5中加入classList 一系列操作 兼容至IE10
DOM相关
dom事件模型
DOM之事件模型分脚本模型、内联模型(同类一个,后者覆盖)、动态绑定(同类多个) demo
<body> <!--行内绑定:脚本模型--> <button onclick="javascrpt:alert('Hello')">Hello1</button> <!--内联模型--> <button onclick="showHello()">Hello2</button> <!--动态绑定--> <button id="btn3">Hello3</button> </body> <script> /*DOM0:同一个元素,同类事件只能添加一个,如果添加多个, * 后面添加的会覆盖之前添加的*/ function shoeHello() { alert("Hello"); } var btn3=document.getElementById("btn3"); btn3.onclick=function () { alert("Hello"); } /*DOM2:可以给同一个元素添加多个同类事件*/ btn3.addEventListener("click",function () { alert("hello1"); }); btn3.addEventListener("click",function () { alert("hello2"); }) if (btn3.attachEvent){ /*IE*/ btn3.attachEvent("onclick",function () { alert("IE Hello1"); }) }else { /*W3C*/ btn3.addEventListener("click",function () { alert("W3C Hello"); }) } </script>
冒泡解释:当点击一个元素触发事件时. 事件会先从元素的最外层父元素一层一层进入到触发的元素, 然后在从触发元素一层一层返回到最外层父元素, 从最外层一层一层进入的阶段叫事件捕获阶段, 从最里层一层一层往外的阶段叫事件冒泡,
移动端触摸事件
①touchstart:当手指触碰到屏幕的时候触发 ②touchmove:当手指在屏幕上滑动的时候触发 ③touchend:当手指离开屏幕的时候时候触发 ④touchcancel事件:当系统停止跟踪触摸的时候触发(这个事件很少会用,一般不做深入研究)。 电话接入或者弹出信息等其他事件切入 event:
每个touch对象包含的属性
事件委托
参考定义:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件 好处:给重复的节点添加相同操作,减少dom交互,提高性能 实现思路:给父组件添加事件,通过事件冒泡,排查元素是否为指定元素,并进行系列操作
HTTP相关
1 常见状态码
2开头 (请求成功)表示成功处理了请求的状态代码。
200 (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。 201 (已创建) 请求成功并且服务器创建了新的资源。 202 (已接受) 服务器已接受请求,但尚未处理。 203 (非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。 204 (无内容) 服务器成功处理了请求,但没有返回任何内容。 205 (重置内容) 服务器成功处理了请求,但没有返回任何内容。 206 (部分内容) 服务器成功处理了部分 GET 请求。
3开头 (请求被重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。
300 (多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。 301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。 302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。 303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。 304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。 305 (使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。 307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
4开头 (请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。
400 (错误请求) 服务器不理解请求的语法。 401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 403 (禁止) 服务器拒绝请求。 404 (未找到) 服务器找不到请求的网页。 405 (方法禁用) 禁用请求中指定的方法。 406 (不接受) 无法使用请求的内容特性响应请求的网页。 407 (需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。 408 (请求超时) 服务器等候请求时发生超时。 409 (冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。 410 (已删除) 如果请求的资源已永久删除,服务器就会返回此响应。 411 (需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。 412 (未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。 413 (请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。 414 (请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。 415 (不支持的媒体类型) 请求的格式不受请求页面的支持。 416 (请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。 417 (未满足期望值) 服务器未满足"期望"请求标头字段的要求。
5开头(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
500 (服务器内部错误) 服务器遇到错误,无法完成请求。 501 (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。 502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。 503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。 504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。
缓存
2. cdn
Cache-Control 和 Etag 的区别
如下图
Cookie sessionStorage localStorage
共同点:都是保存在浏览器端,且同源的。 区别:cookie数据始终在同源的http请求中携带,即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据不能超过4k(适合保存小数据)。 sessionStorage和localStorage容量较大,数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效。localStorage:始终有效,窗口或浏览器关闭也一直保存,需手动清楚;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。作用域不同。 sessionStorage不在不同的浏览器窗口中共享;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。
应用场景:localStorage:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据。sessionStorage :敏感账号一次性登录; cookies与服务器交互。
GET POST区别
请求行,请求头,请求体详解
1,2,3请求行,4请求体,5请求体跨域、JSONP 、CORS、postMessage
跨域概念解释:当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指的是这样的一个概念:我们认为若协议 + 域名 + 端口号均相同,那么就是同域。 如下表
jsonp实现
原生 <script> var script=document.createElement('script'); script.type='text/javascript'; // 传参并指定回调执行函数为onBack script.src='http://www.domain2.com:8080/login?user=admin&callback=onBack'; document.head.appendChild(script); // 回调执行函数 function onBack(res) { alert(JSON.stringify(res)); } </script> jquery $.ajax({ url: 'http://www.domain2.com:8080/login', type: 'get', dataType: 'jsonp', // 请求方式为jsonp jsonpCallback: "onBack", // 自定义回调函数名 data: {} }); vue this.$http.jsonp('http://www.domain2.com:8080/login', { params: {}, jsonp: 'onBack' }).then((res)=> { console.log(res); }) 配合的后端node实现,其他服务器语言也可以 const querystring=require('querystring'); const http=require('http'); const server=http.createServer(); server.on('request', function(req, res) { var params=qs.parse(req.url.split('?')[1]); var fn=params.callback; // jsonp返回设置 res.writeHead(200, { 'Content-Type': 'text/javascript' }); res.write(fn + '(' + JSON.stringify(params) + ')'); res.end(); }); server.listen('8080'); jsoup缺点只能实现get请求
CORS:跨源资源共享 Cross-Origin Resource Sharing(CORS),通常服务器设置,若带cookie请求,则前后端都需要设置 后端常见设置 response.setHeader("Access-Control-Allow-Origin", "www.domain1.com"); // 若有端口需写全(协议+域名+端口),允许那些外源请求 response.setHeader("Access-Control-Allow-Credentials", "true"); //是否需要验证
前端示例
原生 var xhr=new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容 // 前端设置是否带cookie xhr.withCredentials=true; xhr.open('post', 'http://www.domain2.com:8080/login', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('user=admin'); xhr.onreadystatechange=function() { if (xhr.readyState==4 && xhr.status==200) { alert(xhr.responseText); } jquery $.ajax({ ... xhrFields: { withCredentials: true // 前端设置是否带cookie }, crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie ... }); postMessage(data,origin)方法接受两个参数 demo a.html <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe=document.getElementById('iframe'); iframe.onload=function() { var data={ name: 'aym' }; // 向domain2传送跨域数据 iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com'); }; // 接受domain2返回数据 window.addEventListener('message', function(e) { alert('data from domain2 ---> ' + e.data); }, false); </script> b.html 与a.html不同源 <script> // 接收domain1的数据 window.addEventListener('message', function(e) { alert('data from domain1 ---> ' + e.data); var data=JSON.parse(e.data); if (data) { data.number=16; // 处理后再发回domain1 window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com'); } }, false); </script>
osi模型
七层结构:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层 tcp ucp属于传输层;http属于应用层
http2.0 http1
Vue相关
生命周期顺序
组件通信
1.父传子用props,父用子用ref 子调父用$emit,无关系用Bus
Vuex
组件通信库,可以避免子组件无法改变props的弊端等 mutations 同步操作, 用于改变状态 官方不推荐异步 action 执行多个mutaions,官方推荐异步操作 mapState、mapGetters、mapActions使用示例
<template> <el-dialog :visible.sync="show"></el-dialog> </template> <script> import {mapState} from 'vuex'; export default { computed:{ //这里的三点叫做 : 扩展运算符 ...mapState({ show:state=>state.dialog.show }), } } </script> 后两者类似
VueRouter
定义 var routes=[ { path:"/one", component:导入的组件1 }, { path:"/two", component:导入的组件2 }, ]; // 定义路由组件 var router=new VueRouter({ routes }); // 定义路由 new Vue({ el:"#box", router }); 访问设定的路由后 会将<router-view></router-view>替换成相应的模版 html访问方式 <router-link to="/one">One</router-link>(类似a标签) js访问方式 this.$router.push('/one'); replace方式 替换当前页面 携带的参数 可以通过this.$route.query.xxxx来获取
Vue双向绑定
原理:利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。 缺点:双向数据流是自动管理状态的, 但是在实际应用中会有很多不得不手动处理状态变化的逻辑, 使得程序复杂度上升, 难以调试。
computed watch methods
用法: 区别:
算法相关
各种排序实现
相关数据
// 冒泡排序: 比较两个相邻的项,如果第一个大于第二个则交换他们的位置,元素项向上移动至正确的顺序,就好像气泡往上冒一样 冒泡demo: function bubbleSort(arr) { let len=arr.length; for (let i=0; i < len; i++) { for (let j=0; j < len - 1 - i; j++) { if (arr[j] > arr[j+1]) { //相邻元素两两对比 [arr[j + 1], arr[j]]=[arr[j], arr[j + 1]]; } } } return arr; } // 1) 首先,在数组中选择一个中间项作为主元 // 2) 创建两个指针,左边的指向数组第一个项,右边的指向最后一个项,移动左指针,直到找到一个比主元大的项,接着,移动右边的指针,直到找到一个比主元小的项,然后交换它们。重复这个过程,直到 // 左侧的指针超过了右侧的指针。这个使比主元小的都在左侧,比主元大的都在右侧。这一步叫划分操作 // 3) 接着,算法对划分后的小数组(较主元小的值组成的的小数组, 以及较主元大的值组成的小数组)重复之前的两个步骤,直到排序完成 快排demo: function quickSort(arr, left, right) { let len=arr.length; let partitionIndex; left=typeof left !=='number' ? 0 : left; right=typeof right !=='number' ? len - 1 : right; if (left < right) { partitionIndex=partition(arr, left, right); quickSort(arr, left, partitionIndex - 1); quickSort(arr, partitionIndex + 1, right); } return arr; } function partition(arr, left, right) { //分区操作 let pivot=left; //设定基准值(pivot) let index=pivot + 1; for (let i=index; i <=right; i++) { if (arr[i] < arr[pivot]) { [arr[i], arr[index]]=[arr[index], arr[i]]; index++; } } [arr[pivot], arr[index - 1]]=[arr[index - 1], arr[pivot]]; return index - 1; } // 选择排序:大概思路是找到最小的放在第一位,找到第二小的放在第二位,以此类推 算法复杂度O(n^2) 选择demo: function selectionSort(arr) { let len=arr.length; let minIndex; for (let i=0; i < len - 1; i++) { minIndex=i; for (let j=i + 1; j < len; j++) { if (arr[j] < arr[minIndex]) { //寻找最小的数 minIndex=j; //将最小数的索引保存 } } [arr[i], arr[minIndex]]=[arr[minIndex], arr[i]]; } return arr; } // 插入排序:每次排一个数组项,假设数组的第一项已经排序,接着,把第二项与第一项进行对比,第二项是该插入到第一项之前还是之后,第三项是该插入到第一项之前还是第一项之后还是第三项 插入demo: function insertionSort(arr) { let len=arr.length; let preIndex, current; for (let i=1; i < len; i++) { preIndex=i - 1; current=arr[i]; while (preIndex >=0 && arr[preIndex] > current) { arr[preIndex + 1]=arr[preIndex]; preIndex--; } arr[preIndex + 1]=current; } return arr; } // 归并排序:Mozilla Firefox 使用归并排序作为Array.prototype.sort的实现,而chrome使用快速排序的一个变体实现的,前面三种算法性能不好,但归并排序性能不错 算法复杂度O(nlog^n) // 归并排序是一种分治算法。本质上就是把一个原始数组切分成较小的数组,直到每个小数组只有一个位置,接着把小数组归并成较大的数组,在归并过程中也会完成排序,直到最后只有一个排序完毕的大数组 归并demo: function mergeSort(arr) { //采用自上而下的递归方法 let len=arr.length; if(len < 2) { return arr; } let middle=Math.floor(len / 2), left=arr.slice(0, middle), right=arr.slice(middle); return merge(mergeSort(left), mergeSort(right)); } function merge(left, right){ let result=[]; while (left.length && right.length) { if (left[0] <=right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } result.push(...left); result.push(...right); return result; } //堆排序:堆排序把数组当中二叉树来排序而得名。 // 1)索引0是树的根节点;2)除根节点为,任意节点N的父节点是N/2;3)节点L的左子节点是2*L;4)节点R的右子节点为2*R + 1 // 本质上就是先构建二叉树,然后把根节点与最后一个进行交换,然后对剩下对元素进行二叉树构建,进行交换,直到剩下最后一个 堆demo: var len; //因为声明的多个函数都需要数据长度,所以把len设置成为全局变量 function buildMaxHeap(arr) { //建立大顶堆 len=arr.length; for (let i=Math.floor(len / 2); i >=0; i--) { heapify(arr, i); } } function heapify(arr, i) { //堆调整 let left=2 * i + 1; let right=2 * i + 2; let largest=i; if (left < len && arr[left] > arr[largest]) { largest=left; } if (right < len && arr[right] > arr[largest]) { largest=right; } if (largest !==i) { [arr[i], arr[largest]]=[arr[largest], arr[i]]; heapify(arr, largest); } } function heapSort(arr) { buildMaxHeap(arr); for (let i=arr.length - 1; i > 0; i--) { [arr[0],arr[i]]=[arr[i],arr[0]]; len--; heapify(arr, 0); } return arr; }
二分查找
思路 (1)首先,从有序数组的中间的元素开始搜索,如果该元素正好是目标元素(即要查找的元素),则搜索过程结束,否则进行下一步。 (2)如果目标元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半区域查找,然后重复第一步的操作。 (3)如果某一步数组为空,则表示找不到目标元素。
// 非递归算法 function binary_search(arr, key) { let low=0; let high=arr.length - 1; while(low <=high){ let mid=parseInt((high + low) / 2); if(key===arr[mid]){ return mid; }else if(key > arr[mid]){ low=mid + 1; }else if(key < arr[mid]){ high=mid -1; }else{ return -1; } } } // 递归算法 function binary_search(arr,low, high, key) { if (low > high){ return -1; } let mid=parseInt((high + low) / 2); if(arr[mid]===key){ return mid; }else if (arr[mid] > key){ high=mid - 1; return binary_search(arr, low, high, key); }else if (arr[mid] < key){ low=mid + 1; return binary_search(arr, low, high, key); } }; 复制代码
二叉树相关
创建 function Node(data,left,right){ this.data=data;//数值 this.left=left;//左节点 this.right=right;//右节点 }; 插入二叉树 function insert(node,data){ //创建一个新的节点 let newNode=new Node(data,null,null); //判断是否存在根节点,没有将新节点存入 if(node==null){ node=newNode; }else{ //获取根节点 let current=node; let parent; while(true){ //将当前节点保存为父节点 parent=current; //将小的数据放在左节点 if(data < current.data){ //获取当前节点的左节点 //判断当前节点下的左节点是否有数据 current=current.left; if(current==null){ //如果没有数据将新节点存入当前节点下的左节点 parent.left=newNode; break; } }else{ current=current.right; if(current==null){ parent.right=newNode; break; } } } } } 翻转二叉树 function invertTree(node) { if (node !==null) { node.left, node.right=node.left, node.right; invertTree(node.left); invertTree(node.right); } return node; } 查找链表中倒数第k个结点 2个思路 1:先遍历出长度,然后查找长度-k+1的值 2:2个指针,一个指针先走k-1,然后两个一起走到底部,后者就是结果
网络安全相关
XSS CSRF
XSS(跨站脚本攻击),恶意的注入html代码,其他用户访问时,会被执行 特点:能注入恶意的HTML/JavaScript代码到用户浏览的网页上,从而达到Cookie资料窃取、会话劫持、钓鱼欺骗等攻击 防御手段:
webpack相关
#####打包体积 优化思路
Loader
编写一个loader
loader就是一个node模块,它输出了一个函数。当某种资源需要用这个loader转换时,这个函数会被调用。并且,这个函数可以通过提供给它的this上下文访问Loader API。 reverse-txt-loader 定义 module.exports=function(src) { //src是原文件内容(abcde),下面对内容进行处理,这里是反转 var result=src.split('').reverse().join(''); //返回JavaScript源码,必须是String或者Buffer return `module.exports='${result}'`; } 使用 { test: /\.txt$/, use: [ { './path/reverse-txt-loader' } ] },
plugins
使用范围更广,通常只需要require()然后添加到plugins数组中,且需要new一个
其他
URL到界面显示发生了什么
POST / HTTP1.1 Host:www.wrox.com User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) Content-Type:application/x-www-form-urlencoded Content-Length:40 Connection: Keep-Alive name=Professional%20Ajax&publisher=Wiley 第一部分:请求行,第一行说明是post请求,以及http1.1版本。 第二部分:请求头部,第二行至第六行。 第三部分:空行,第七行的空行。 第四部分:请求数据,第八行。 4. 服务器处理请求并返回HTTP报文 后端处理返回http报文如下 HTTP/1.1 200 OK Date: Fri, 22 May 2009 06:07:21 GMT Content-Type: text/html; charset=UTF-8 <html> <head></head> <body> <!--body goes here--> </body> </html> 第一行为状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为(ok) 第二行和第三行为消息报头, Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8 第三部分:空行,消息报头后面的空行是必须的 第四部分:响应正文,服务器返回给客户端的文本信息。 空行后面的html部分为响应正文。
组件封装
目的:为了重用,提高开发效率和代码质量 注意:低耦合,单一职责,可复用性,可维护性 常用操作:
JS异步加载
css与js动画差异
负载均衡
多台服务器共同协作,不让其中某一台或几台超额工作,发挥服务器的最大作用
CDN
内容分发网络,基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。
内存泄漏
定义:程序中己动态分配的堆内存由于某种原因程序未释放或无法释放引发的各种问题 js中可能出现的内存泄漏情况 结果:变慢,崩溃,延迟大等 原因:
避免策略:
babel原理
ES6、7代码输入 -> babylon进行解析 -> 得到AST(抽象语法树)-> plugin用babel-traverse对AST树进行遍历转译 ->得到新的AST树->用babel-generator通过AST树生成ES5代码、
promise
特性:Promise 对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止,也即是说,错误总会被下一个catch语句捕获
js自定义事件
三要素: document.createEvent() event.initEvent() element.dispatchEvent()
demo: (en:自定义事件名称,fn:事件处理函数,addEvent:为DOM元素添加自定义事件,triggerEvent:触发自定义事件) window.onload=function(){ var demo=document.getElementById("demo"); demo.addEvent("test",function(){console.log("handler1")}); demo.addEvent("test",function(){console.log("handler2")}); demo.onclick=function(){ this.triggerEvent("test"); } } Element.prototype.addEvent=function(en,fn){ this.pools=this.pools || {}; if(en in this.pools){ this.pools[en].push(fn); }else{ this.pools[en]=[]; this.pools[en].push(fn); } } Element.prototype.triggerEvent=function(en){ if(en in this.pools){ var fns=this.pools[en]; for(var i=0,il=fns.length;i<il;i++){ fns[i](); } }else{ return; } }
es6模块 commonjs amd cmd
前后端路由差别
1.后端每次路由请求都是重新访问服务器
2.前端路由实际上只是JS根据URL来操作DOM元素,根据每个页面需要的去服务端请求数据,返回数据后和模板进行组合。
文从移动互联网广告的定义、类型、策略、趋势等角度,做了全局梳理与分析。
互联网行业发展日新月异,互联网广告作为“伴生物”,也随行业巨头、行业风口流转和追随。
其中最重要的转折莫过于,在4G大范围普及,用户注意力逐渐转移到手机、平板电脑等移动设备的背景下,移动互联网逐渐成为行业主流,互联网广告亦是如此。
据艾瑞咨询数据,从2013到2019年,移动广告占网络广告的比重从12.1%一路暴涨至82.8%,已成网络广告的核心。
移动广告的崛起引人注目,趁着2019即将收尾,本文就当前移动互联网广告定义、类型和策略做一次全面的梳理和总结,并嗅探未来趋势。
本文框架图
移动互联网广告是通过移动设备(手机、PSP、平板电脑等)访问移动APP或移动网页时显示的广告。
具体来说,即Wap广告、APP广告两类,这两种广告投放形式和展现模式有着很大的差异,前者依托于html架构,是展示在在浏览器网页端的广告,由网站的开发者获取收益,份额占比较少,后者依托于OS操作系统架构,是展现在APP应用里面的广告,是主流形式。
Wap广告姑且不提,据笔者观察,目前业内有两种APP广告划分方法:
按照APP所属行业进行划分,可以分为搜索广告、电商广告、门户及新闻广告、垂直广告(即竖屏视频)、视频广告(即横屏视频)等。这种划分形式适用于研究、分析机构,用来洞察各行业发展趋势,比如18年抖音、快手APP的崛起,就极大的推升了垂直广告份额的增长。
按照广告位的属性进行划分,可以分为开屏广告、横幅广告、插屏广告、贴片广告、信息流广告、激励广告等。在笔者看来,这种划分形式最为准确,广告位是广告主在广告投放中直接的采买物,同一APP内也往往拥有多个广告位,同一广告位也有着多种形式。
开屏广告(Splash Ad.)也被称为启动页广告,出现在 APP 启动加载时,将广告图片或视频展示固定时间(5秒),展示完毕后自动关闭并进入APP主页面。
作为进入载体app的首要入口,开屏广告在设计上有着尺寸大(APP内置最大的广告位)、可跳过(一般放置于用户较少触碰的位置)的特点,基于此,开屏广告的广告可见性高、广告效果好,是品牌广告主的首选。
横幅广告(Banner Ad.)是网络广告最早采用的形式,也一直延用至移动端,以文字、图片等富媒体形式,在 APP 首页、发现页、专题详情页等页面的顶部(含下拉刷新)、底部或中部呈现。
相对于开屏广告,横幅广告在移动端设计上有着尺寸小、位置偏(照顾用户体验、避免误触)、支持轮播(动态展示不同广告主的文字链/图片)的特点,基于此,横幅广告难以吸引用户注意力,广告可见性和广告效果都较差。
插屏广告(Interstitial Ad.)是触发式广告,在用户做出相应的操作(如开启、暂停、过关、跳转、退出)后,弹出的以图片、动图、视频等为表现形式的半屏或全屏广告。
相较于横幅广告,插屏广告曝光性强,很吸引用户注意力,但容易引起用户反感(打断用户正常使用APP的操作),立即关掉广告。整体来看,插屏广告影响用户体验,广告可见性和广告效果都很一般。
贴片广告(Roll Ad.)即将广告内容贴入视频之中。可以分为视频贴片和创可贴两种形式,前者是将5s-60s不等的横版视频广告,添加至视频播放前、视频播放中或视频播放后这三个位置,后者将图片/动图等元素放在正在播放的视频中。
视频贴片广告沿袭了PC时代的广告位设计,整体广告效果和视频类型和质量相关较为密切,一般以传统采买、竞标的交易模式,而创可贴广告可结合剧情设计,尺寸小的特点,使得用户体验更好,广告可见性更高、广告效果更好。
信息流广告(Feeds Ad.)是当前APP最流行的形式,出现于有内容产出的APP,是与APP的日常内容(如一则资讯、动态、图片、视频)融为一体的广告形式。
相较于其他广告位,信息流广告是延展性、可玩性最强的广告位,目前可记录的形式就有数十种,文字链,小图、组图、大图、竖版视频等等。
由于原生性、内容性较强,信息流广告能够最大限度地保护用户体验,还能形成二次传播,广告效果优于大多数广告位。
搜索广告(Search Ad.)也是触发式广告,用户搜索关键词后,在搜索联想、搜索结果中出现广告,一般为广告主的APP/商品,或者是带有推广性质的内容,以信息流的形式呈现。一般是应用商店,电商,搜索工具类的主流广告位,近年来,大型社交、资讯类APP也在加快布局。
不可置否,搜索广告是转化效果最强的广告位,能够形成用户搜索——广告引导——完成转化(下载/下单)的环路。但提供搜索广告的APP,需要有一定的搜索基数以及口碑(用户信赖度),所以目前在移动广告中,搜索广告是应用门槛最高的广告位。
激励广告(Incentive Ad.),是利用激励让用户接受广告或做出指定行为,比如下载APP、观看视频等。可以分为积分墙和激励视频两种形式,前者用户可以完成指定操作获取积分,并兑换奖励,后者则让用户完成指定操作,获取权益,比如游戏复活,新增特权等。
互惠互利是激励广告的核心特点,对于APP来说,提供激励广告,能够对付费意愿不强的用户流量变现,对于用户来说,能通过操作获取相应的权益,提升APP使用体验。整体来看,激励广告的点击成本低,广告效果好。
策略,是据形势发展而制定的行动方针和斗争方法。过去部分文章中经常将提升广告效果的策略,如原生广告,定性为可售卖的广告位、混为一谈。本部分笔者重新梳理和定义这些新策略:
原生广告诞生于2011年、爆红于2013年,发源于国外,流行至全球。
Solve Media给出的定义是:“原生广告是指一种通过在信息流里发布具有相关性的内容产生价值,提升用户体验的特定商业模式。具体实现形式是将广告作为内容的一部分植入到实际APP(PC)页面设计中。
IDEAinside的创始人认为原生广告有价值性、原生性、主动性这三个特点,简而言之,原生广告的内容是有价值的有意义,且和页面融为一体,能够让用户乐于阅读和分享。
原生广告的原理,经过近5年的发展和实践,已经有了详实的应用方式和案例:
从国内的发展节奏来看,互动广告于16年年底摸索,在17年发迹、爆发,于18年行业井喷,并不断涌现出新的形式和玩法。
互动广告是一种广告载体或广告形式,其最大的价值是挖掘出了一个新的流量场景,并通过和用户互动交流的方法,搭建广告主和用户之间的沟通桥梁。
不同于原生广告融入当代广告设计理念,互动广告已经形成了完善的行业产业链,目前国内有豆盟(19年已上市)、推啊、互动推等数十家互动广告企业,App开发者接入一个互动广告SDK,交由互动广告平台分发售卖广告,据笔者观察,可以分为以下两大类:
传统活动广告:广告即“活动”,用户可点击APP上的活动icon(实质上是广告位),进入活动H5参与小游戏,例如“转盘抽奖、砸金蛋抽奖、刮刮卡抽奖”等,获取广告主的权益或福利,引导转化。
互动视频广告:本质上也是活动广告,但“青出于蓝”,交互式体验比较强,不仅流行,可玩性、定制性也强:
“一镜到底”是指拍摄中没有cut情况,运用一定技巧将作品一次性拍摄完成的拍摄手法,常见于一些短视频,当它跨界到广告中,会碰撞出什么样的火花呢?2019年的联动广告给了我们答案。
联动广告是将多个广告位进行联动展示同一广告主的创意的形式,持续吸引用户的注意力,目前常见的是开屏广告和信息流广告的组合,有以下特点:
可以预见的是,联动广告在未来会结合营销需要,推出更多新的形式,诸如多个信息流广告位、多页面广告位的联动,将重复曝光,多创意形态、传播模式的营销价值发挥得淋漓尽致。
据艾瑞咨询发布的《2019年中国网络广告市场分析报告》,2018年移动广告市场规模达到3663.0亿元,移动广告的整体市场增速远远高于网络广告市场增速,未来,预计2021年移动广告占网络广告的比例将超过85%。
移动广告份额继续扩大的同时,程序化正在以前所未有的速度流行起来。通过InMobi Exchange的数据预估,2019年全球程序化移动广告支出增长将超过250%,而中国程序化移动广告支出增长也将达到111%,突破1900亿人民币。越来越多的广告交易通过程序化购买的方式产生。
据CTR媒介智讯发布的《2019Q3中国广告市场》的数据,经济环境影响下,广告主对2019年整体经济市场的信心有所波动,致使中国广告市场重新进入调整期。截至2019年前三季度,中国广告市场整体下滑8.0%,近4年来首次下滑。
预算减少了,广告主选择把钱花在更有效果的媒体上。过去,品牌广告是大多数广告主的选择,广撒网,寻求长效影响力,价格不菲。而在经济寒冬期,由于效果广告能够直接导向广告主销售KPI,因此也成为了企业的重点。诸如上文中提到的互动广告、联动广告的营销玩法,也得到了广泛的开拓和应用。
历经4年的发展,大流量平台纷纷布局小程序领域,更多类型的小程序将接入各平台。据iiMedia Research(艾媒咨询)数据显示,2019年中国小程序数量预计超650万个,到2020年预计将超1400万个。
小程序是基于网页开发、不需要下载安装即可使用的平台内应用,不管是广告主还是移动APP开发者都在布局,在微信、支付宝、百度推出自己的小程序。基于小程序的推广和变现,也可以窥其一二:
虚假广告治理:对虚假广告的治理一直是国家相关部门的重点工作,近期沸沸扬扬的“欧莱雅”因虚假广告被罚,移动互联网不是法外之地,一旦传播虚假广告,媒体方等广告服务方也将一同担责,互联网广告准入机制已成形。
用户隐私保护:4-5月,相关部门先后发布《互联网个人信息安全保护指南》、《数据安全管理办法(征求意见稿)》,明确指出用户有拒绝精准广告的权力,推动互联网数据安全保护有法可依。
虚假流量监测:全国互联网广告监测中心正式启动2年来,目前已实现对全国46个副省级以上行政区划的1004家重点网站及4家广告联盟和电商平台广告数据的监测,互联网广告的违法率从开展监测前的7.1%降至1.98%。
“关注变化的,守住不变的。”
在广告圈,年底贩卖焦虑的文章一时风起云涌,“我太难(南了)”似乎成为了广告人、广告行业、广告代理公司的真实写照。艰难、焦虑、浮躁不如回归本心,不如理清思路,重新出发。
作者:AdBright,公众号(id:AdBright01),程序化广告新人,分享相关程序化广告技术、产品、市场知识。
本文由 @AdBright 原创发布于人人都是产品经理,未经许可,禁止转载。
题图来自Unsplash,基于CC0协议。
*请认真填写需求信息,我们会在24小时内与您取得联系。